mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-04-30 06:14:25 +02:00
Initial commit
This commit is contained in:
parent
f3cce6a38e
commit
ea16cbbb19
29
build.gradle
Normal file
29
build.gradle
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
apply plugin: 'java-library'
|
||||
|
||||
group 'com.reandroid.lib.arsc'
|
||||
version '1.0.0'
|
||||
|
||||
java {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
if (JavaVersion.current().isJava8Compatible()) {
|
||||
allprojects {
|
||||
tasks.compileJava {
|
||||
//options.addStringOption('-Xlint:unchecked', '-quiet')
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
}
|
||||
|
||||
processResources.inputs.property('version', version)
|
||||
processResources.expand('version': version)
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https://services.gradle.org/distributions/gradle-6.5.1-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
2
settings.gradle
Normal file
2
settings.gradle
Normal file
@ -0,0 +1,2 @@
|
||||
rootProject.name = 'ARSCLib'
|
||||
|
41
src/main/java/com/reandroid/lib/arsc/BuildInfo.java
Normal file
41
src/main/java/com/reandroid/lib/arsc/BuildInfo.java
Normal file
@ -0,0 +1,41 @@
|
||||
package com.reandroid.lib.arsc;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
public class BuildInfo {
|
||||
private static Properties sProperties;
|
||||
|
||||
public static String getName(){
|
||||
Properties properties=getProperties();
|
||||
return properties.getProperty("lib.name", "ARSCLib");
|
||||
}
|
||||
public static String getVersion(){
|
||||
Properties properties=getProperties();
|
||||
return properties.getProperty("lib.version", "");
|
||||
}
|
||||
public static String getRepo(){
|
||||
Properties properties=getProperties();
|
||||
return properties.getProperty("lib.repo", "https://github.com/REAndroid");
|
||||
}
|
||||
public static String getDescription(){
|
||||
Properties properties=getProperties();
|
||||
return properties.getProperty("lib.description", "Failed to load properties");
|
||||
}
|
||||
|
||||
private static Properties getProperties(){
|
||||
if(sProperties==null){
|
||||
sProperties=loadProperties();
|
||||
}
|
||||
return sProperties;
|
||||
}
|
||||
private static Properties loadProperties(){
|
||||
InputStream inputStream=BuildInfo.class.getResourceAsStream("/lib.properties");
|
||||
Properties properties=new Properties();
|
||||
try{
|
||||
properties.load(inputStream);
|
||||
}catch (Exception ignored){
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.value.EntryBlock;
|
||||
|
||||
|
||||
public class EntryBlockArray extends OffsetBlockArray<EntryBlock> {
|
||||
public EntryBlockArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart){
|
||||
super(offsets, itemCount, itemStart);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public EntryBlock newInstance() {
|
||||
return new EntryBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntryBlock[] newInstance(int len) {
|
||||
return new EntryBlock[len];
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.value.LibraryInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class LibraryInfoArray extends BlockArray<LibraryInfo> {
|
||||
private final IntegerItem mInfoCount;
|
||||
public LibraryInfoArray(IntegerItem infoCount){
|
||||
this.mInfoCount=infoCount;
|
||||
}
|
||||
@Override
|
||||
public LibraryInfo newInstance() {
|
||||
return new LibraryInfo();
|
||||
}
|
||||
@Override
|
||||
public LibraryInfo[] newInstance(int len) {
|
||||
return new LibraryInfo[len];
|
||||
}
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
mInfoCount.set(childesCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
setChildesCount(mInfoCount.get());
|
||||
super.onReadBytes(reader);
|
||||
}
|
||||
}
|
177
src/main/java/com/reandroid/lib/arsc/array/OffsetBlockArray.java
Normal file
177
src/main/java/com/reandroid/lib/arsc/array/OffsetBlockArray.java
Normal file
@ -0,0 +1,177 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.io.BlockLoad;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.ByteArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public abstract class OffsetBlockArray<T extends Block> extends BlockArray<T> implements BlockLoad {
|
||||
private final IntegerArray mOffsets;
|
||||
private final IntegerItem mItemStart;
|
||||
private final IntegerItem mItemCount;
|
||||
private final ByteArray mEnd4Block;
|
||||
private byte mEnd4Type;
|
||||
public OffsetBlockArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart){
|
||||
super();
|
||||
this.mOffsets=offsets;
|
||||
this.mItemCount=itemCount;
|
||||
this.mItemStart=itemStart;
|
||||
this.mEnd4Block=new ByteArray();
|
||||
mItemCount.setBlockLoad(this);
|
||||
}
|
||||
void setEndBytes(byte b){
|
||||
this.mEnd4Type=b;
|
||||
this.mEnd4Block.fill(b);
|
||||
}
|
||||
@Override
|
||||
public int countBytes(){
|
||||
int result=super.countBytes();
|
||||
int endCount=mEnd4Block.countBytes();
|
||||
return result+endCount;
|
||||
}
|
||||
@Override
|
||||
public byte[] getBytes(){
|
||||
byte[] results=super.getBytes();
|
||||
if(results==null){
|
||||
return null;
|
||||
}
|
||||
byte[] endBytes=mEnd4Block.getBytes();
|
||||
results=addBytes(results, endBytes);
|
||||
return results;
|
||||
}
|
||||
@Override
|
||||
public int onWriteBytes(OutputStream stream) throws IOException {
|
||||
int result=super.onWriteBytes(stream);
|
||||
if(result==0){
|
||||
return 0;
|
||||
}
|
||||
result+=mEnd4Block.writeBytes(stream);
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
mOffsets.setSize(childesCount());
|
||||
T[] childes=getChildes();
|
||||
int sum=0;
|
||||
if(childes!=null){
|
||||
int max=childes.length;
|
||||
for(int i=0;i<max;i++){
|
||||
T item=childes[i];
|
||||
int offset;
|
||||
if(item==null || item.isNull()){
|
||||
offset=-1;
|
||||
}else {
|
||||
offset=sum;
|
||||
sum+=item.countBytes();
|
||||
}
|
||||
mOffsets.put(i, offset);
|
||||
}
|
||||
}
|
||||
refreshCount();
|
||||
refreshStart();
|
||||
refreshEnd4Block();
|
||||
}
|
||||
private void refreshCount(){
|
||||
mItemCount.set(childesCount());
|
||||
}
|
||||
private void refreshStart(){
|
||||
Block parent=getParent();
|
||||
if(parent==null){
|
||||
return;
|
||||
}
|
||||
int start=parent.countUpTo(this);
|
||||
mItemStart.set(start);
|
||||
}
|
||||
void refreshEnd4Block(BlockReader reader, ByteArray end4Block) throws IOException{
|
||||
refreshEnd4Block();
|
||||
}
|
||||
void refreshEnd4Block(ByteArray end4Block){
|
||||
if(childesCount()==0){
|
||||
end4Block.clear();
|
||||
return;
|
||||
}
|
||||
int count=countBytes();
|
||||
if(count%4==0){
|
||||
return;
|
||||
}
|
||||
end4Block.clear();
|
||||
count=countBytes();
|
||||
int add=0;
|
||||
int rem=count%4;
|
||||
while (rem!=0){
|
||||
add++;
|
||||
count++;
|
||||
rem=count%4;
|
||||
}
|
||||
end4Block.setSize(add);
|
||||
end4Block.fill(mEnd4Type);
|
||||
}
|
||||
private void refreshEnd4Block(){
|
||||
refreshEnd4Block(mEnd4Block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException{
|
||||
T[] childes=getChildes();
|
||||
if(childes==null||childes.length==0){
|
||||
return;
|
||||
}
|
||||
int[] offsetArray=mOffsets.toArray();
|
||||
int max=childes.length;
|
||||
int start=mItemStart.get();
|
||||
reader.seek(start);
|
||||
int zeroPosition=reader.getPosition();
|
||||
int maxPos=zeroPosition;
|
||||
for(int i=0;i<max;i++){
|
||||
T item=childes[i];
|
||||
int offset=offsetArray[i];
|
||||
if(offset==-1){
|
||||
item.setNull(true);
|
||||
continue;
|
||||
}
|
||||
int itemStart=zeroPosition+offset;
|
||||
reader.seek(itemStart);
|
||||
item.readBytes(reader);
|
||||
int pos=reader.getPosition();
|
||||
if(pos>maxPos){
|
||||
maxPos=pos;
|
||||
}
|
||||
}
|
||||
reader.seek(maxPos);
|
||||
refreshEnd4Block(reader, mEnd4Block);
|
||||
}
|
||||
@Override
|
||||
public void onBlockLoaded(BlockReader reader, Block sender) throws IOException {
|
||||
if(sender==mItemCount){
|
||||
int count=mItemCount.get();
|
||||
setChildesCount(count);
|
||||
mOffsets.setSize(count);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(getClass().getSimpleName());
|
||||
builder.append(": count = ");
|
||||
int s= childesCount();
|
||||
builder.append(s);
|
||||
int count=mItemCount.get();
|
||||
if(s!=count){
|
||||
builder.append(", countValue=");
|
||||
builder.append(count);
|
||||
}
|
||||
builder.append(", start=");
|
||||
builder.append(mItemStart.get());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
42
src/main/java/com/reandroid/lib/arsc/array/PackageArray.java
Normal file
42
src/main/java/com/reandroid/lib/arsc/array/PackageArray.java
Normal file
@ -0,0 +1,42 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.chunk.PackageBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockLoad;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class PackageArray extends BlockArray<PackageBlock> implements BlockLoad {
|
||||
private final IntegerItem mPackageCount;
|
||||
public PackageArray(IntegerItem packageCount){
|
||||
this.mPackageCount=packageCount;
|
||||
mPackageCount.setBlockLoad(this);
|
||||
}
|
||||
@Override
|
||||
public PackageBlock newInstance() {
|
||||
return new PackageBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageBlock[] newInstance(int len) {
|
||||
return new PackageBlock[len];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
refreshPackageCount();
|
||||
}
|
||||
private void refreshPackageCount(){
|
||||
mPackageCount.set(childesCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockLoaded(BlockReader reader, Block sender) throws IOException {
|
||||
if(sender==mPackageCount){
|
||||
setChildesCount(mPackageCount.get());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.value.ResValueBagItem;
|
||||
|
||||
public class ResValueBagItemArray extends BlockArray<ResValueBagItem> {
|
||||
public ResValueBagItemArray(){
|
||||
super();
|
||||
}
|
||||
@Override
|
||||
public ResValueBagItem newInstance() {
|
||||
return new ResValueBagItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResValueBagItem[] newInstance(int len) {
|
||||
return new ResValueBagItem[len];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.chunk.xml.ResXmlAttribute;
|
||||
import com.reandroid.lib.arsc.item.ShortItem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ResXmlAttributeArray extends BlockArray<ResXmlAttribute> {
|
||||
private final HeaderBlock mHeaderBlock;
|
||||
private final ShortItem mAttributeStart;
|
||||
private final ShortItem mAttributeCount;
|
||||
public ResXmlAttributeArray(HeaderBlock headerBlock, ShortItem attributeStart, ShortItem attributeCount){
|
||||
this.mHeaderBlock=headerBlock;
|
||||
this.mAttributeStart=attributeStart;
|
||||
this.mAttributeCount=attributeCount;
|
||||
}
|
||||
@Override
|
||||
public ResXmlAttribute newInstance() {
|
||||
return new ResXmlAttribute();
|
||||
}
|
||||
@Override
|
||||
public ResXmlAttribute[] newInstance(int len) {
|
||||
return new ResXmlAttribute[len];
|
||||
}
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
int start=mHeaderBlock.getHeaderSize()+mAttributeStart.get();
|
||||
reader.seek(start);
|
||||
setChildesCount(mAttributeCount.get());
|
||||
super.onReadBytes(reader);
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.chunk.xml.BaseXmlChunk;
|
||||
|
||||
public class ResXmlChunkArray extends BlockArray<BaseXmlChunk> {
|
||||
|
||||
@Override
|
||||
public BaseXmlChunk newInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseXmlChunk[] newInstance(int len) {
|
||||
return new BaseXmlChunk[len];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.ResXmlID;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ResXmlIDArray extends BlockArray<ResXmlID> {
|
||||
private final HeaderBlock mHeaderBlock;
|
||||
private final Map<Integer, ResXmlID> mResIdMap;
|
||||
private boolean mUpdated;
|
||||
public ResXmlIDArray(HeaderBlock headerBlock){
|
||||
super();
|
||||
this.mHeaderBlock=headerBlock;
|
||||
this.mResIdMap=new HashMap<>();
|
||||
}
|
||||
public void addResourceId(int index, int resId){
|
||||
if(index<0){
|
||||
return;
|
||||
}
|
||||
ensureSize(index+1);
|
||||
ResXmlID xmlID=get(index);
|
||||
if(xmlID!=null){
|
||||
xmlID.set(resId);
|
||||
}
|
||||
}
|
||||
public ResXmlID getOrCreate(int resId){
|
||||
updateIdMap();
|
||||
ResXmlID xmlID=mResIdMap.get(resId);
|
||||
if(xmlID!=null){
|
||||
return xmlID;
|
||||
}
|
||||
xmlID=new ResXmlID(resId);
|
||||
add(xmlID);
|
||||
mUpdated=true;
|
||||
mResIdMap.put(resId, xmlID);
|
||||
return xmlID;
|
||||
}
|
||||
public ResXmlID getByResId(int resId){
|
||||
updateIdMap();
|
||||
return mResIdMap.get(resId);
|
||||
}
|
||||
private void updateIdMap(){
|
||||
if(mUpdated){
|
||||
return;
|
||||
}
|
||||
mUpdated=true;
|
||||
mResIdMap.clear();
|
||||
ResXmlID[] allChildes=getChildes();
|
||||
if(allChildes==null||allChildes.length==0){
|
||||
return;
|
||||
}
|
||||
int max=allChildes.length;
|
||||
for(int i=0;i<max;i++){
|
||||
ResXmlID xmlID=allChildes[i];
|
||||
mResIdMap.put(xmlID.get(), xmlID);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public ResXmlID newInstance() {
|
||||
mUpdated=false;
|
||||
return new ResXmlID();
|
||||
}
|
||||
@Override
|
||||
public ResXmlID[] newInstance(int len) {
|
||||
mUpdated=false;
|
||||
return new ResXmlID[len];
|
||||
}
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
int count=calculateCountFromHeaderBlock();
|
||||
setChildesCount(count);
|
||||
super.onReadBytes(reader);
|
||||
updateIdMap();
|
||||
}
|
||||
private int calculateCountFromHeaderBlock(){
|
||||
int count=mHeaderBlock.getChunkSize()-mHeaderBlock.getHeaderSize();
|
||||
count=count/4;
|
||||
return count;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.ResXmlString;
|
||||
|
||||
public class ResXmlStringArray extends StringArray<ResXmlString> {
|
||||
public ResXmlStringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
|
||||
super(offsets, itemCount, itemStart, is_utf8);
|
||||
}
|
||||
@Override
|
||||
public ResXmlString newInstance() {
|
||||
return new ResXmlString(isUtf8());
|
||||
}
|
||||
@Override
|
||||
public ResXmlString[] newInstance(int len) {
|
||||
return new ResXmlString[len];
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.chunk.SpecBlock;
|
||||
|
||||
public class SpecBlockArray extends BlockArray<SpecBlock> {
|
||||
public SpecBlockArray(){
|
||||
super();
|
||||
}
|
||||
@Override
|
||||
public SpecBlock newInstance() {
|
||||
return new SpecBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpecBlock[] newInstance(int len) {
|
||||
return new SpecBlock[len];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.SpecString;
|
||||
|
||||
public class SpecStringArray extends StringArray<SpecString> {
|
||||
public SpecStringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
|
||||
super(offsets, itemCount, itemStart, is_utf8);
|
||||
}
|
||||
@Override
|
||||
public SpecString newInstance() {
|
||||
return new SpecString(isUtf8());
|
||||
}
|
||||
@Override
|
||||
public SpecString[] newInstance(int len) {
|
||||
return new SpecString[len];
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.container.SpecTypePair;
|
||||
|
||||
public class SpecTypePairArray extends BlockArray<SpecTypePair> {
|
||||
public SpecTypePairArray(){
|
||||
super();
|
||||
}
|
||||
@Override
|
||||
public SpecTypePair newInstance() {
|
||||
return new SpecTypePair();
|
||||
}
|
||||
@Override
|
||||
public SpecTypePair[] newInstance(int len) {
|
||||
return new SpecTypePair[len];
|
||||
}
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
|
||||
}
|
||||
}
|
36
src/main/java/com/reandroid/lib/arsc/array/StringArray.java
Normal file
36
src/main/java/com/reandroid/lib/arsc/array/StringArray.java
Normal file
@ -0,0 +1,36 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.StringItem;
|
||||
|
||||
public abstract class StringArray<T extends StringItem> extends OffsetBlockArray<T>{
|
||||
private boolean mUtf8;
|
||||
public StringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
|
||||
super(offsets, itemCount, itemStart);
|
||||
this.mUtf8=is_utf8;
|
||||
setEndBytes((byte)0x00);
|
||||
}
|
||||
public void setUtf8(boolean is_utf8){
|
||||
if(mUtf8==is_utf8){
|
||||
return;
|
||||
}
|
||||
mUtf8=is_utf8;
|
||||
T[] childes=getChildes();
|
||||
if(childes!=null){
|
||||
int max=childes.length;
|
||||
for(int i=0;i<max;i++){
|
||||
childes[i].setUtf8(is_utf8);
|
||||
}
|
||||
}
|
||||
}
|
||||
public boolean isUtf8() {
|
||||
return mUtf8;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshChildes(){
|
||||
// Not required
|
||||
}
|
||||
|
||||
}
|
55
src/main/java/com/reandroid/lib/arsc/array/StyleArray.java
Normal file
55
src/main/java/com/reandroid/lib/arsc/array/StyleArray.java
Normal file
@ -0,0 +1,55 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.ByteArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.StyleItem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class StyleArray extends OffsetBlockArray<StyleItem> {
|
||||
public StyleArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart) {
|
||||
super(offsets, itemCount, itemStart);
|
||||
setEndBytes(END_BYTE);
|
||||
}
|
||||
@Override
|
||||
void refreshEnd4Block(BlockReader reader, ByteArray end4Block) throws IOException {
|
||||
end4Block.clear();
|
||||
if(reader.available()<4){
|
||||
return;
|
||||
}
|
||||
IntegerItem integerItem=new IntegerItem();
|
||||
while (reader.available()>=4){
|
||||
int pos=reader.getPosition();
|
||||
integerItem.readBytes(reader);
|
||||
if(integerItem.get()!=0xFFFFFFFF){
|
||||
reader.seek(pos);
|
||||
break;
|
||||
}
|
||||
end4Block.add(integerItem.getBytes());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
void refreshEnd4Block(ByteArray end4Block) {
|
||||
super.refreshEnd4Block(end4Block);
|
||||
if(childesCount()==0){
|
||||
return;
|
||||
}
|
||||
end4Block.ensureArraySize(8);
|
||||
end4Block.fill(END_BYTE);
|
||||
}
|
||||
@Override
|
||||
protected void refreshChildes(){
|
||||
// Not required
|
||||
}
|
||||
@Override
|
||||
public StyleItem newInstance() {
|
||||
return new StyleItem();
|
||||
}
|
||||
@Override
|
||||
public StyleItem[] newInstance(int len) {
|
||||
return new StyleItem[len];
|
||||
}
|
||||
private static final byte END_BYTE= (byte) 0xFF;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.TableString;
|
||||
|
||||
public class TableStringArray extends StringArray<TableString> {
|
||||
public TableStringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
|
||||
super(offsets, itemCount, itemStart, is_utf8);
|
||||
}
|
||||
@Override
|
||||
public TableString newInstance() {
|
||||
return new TableString(isUtf8());
|
||||
}
|
||||
@Override
|
||||
public TableString[] newInstance(int len) {
|
||||
return new TableString[len];
|
||||
}
|
||||
}
|
127
src/main/java/com/reandroid/lib/arsc/array/TypeBlockArray.java
Normal file
127
src/main/java/com/reandroid/lib/arsc/array/TypeBlockArray.java
Normal file
@ -0,0 +1,127 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.chunk.SpecBlock;
|
||||
import com.reandroid.lib.arsc.chunk.TypeBlock;
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.value.ResConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.AbstractList;
|
||||
import java.util.List;
|
||||
|
||||
public class TypeBlockArray extends BlockArray<TypeBlock> {
|
||||
private byte mTypeId;
|
||||
public TypeBlockArray(){
|
||||
super();
|
||||
}
|
||||
public void setTypeId(byte id){
|
||||
this.mTypeId=id;
|
||||
TypeBlock[] allChildes=getChildes();
|
||||
if(allChildes==null){
|
||||
return;
|
||||
}
|
||||
int max=allChildes.length;
|
||||
for(int i=0;i<max;i++){
|
||||
TypeBlock typeBlock = allChildes[i];
|
||||
typeBlock.setTypeId(id);
|
||||
}
|
||||
}
|
||||
public byte getTypeId(){
|
||||
if(mTypeId != 0){
|
||||
return mTypeId;
|
||||
}
|
||||
SpecBlock specBlock=getSpecBlock();
|
||||
if(specBlock!=null){
|
||||
byte id=specBlock.getTypeId();
|
||||
if(id!=0){
|
||||
mTypeId=id;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
TypeBlock[] allChildes=getChildes();
|
||||
if(allChildes==null){
|
||||
return 0;
|
||||
}
|
||||
int max=allChildes.length;
|
||||
for(int i=0;i<max;i++){
|
||||
TypeBlock typeBlock = allChildes[i];
|
||||
byte id=typeBlock.getTypeId();
|
||||
if(id==0){
|
||||
continue;
|
||||
}
|
||||
if(specBlock!=null){
|
||||
specBlock.setTypeId(id);
|
||||
}
|
||||
mTypeId=id;
|
||||
return id;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public List<ResConfig> listResConfig(){
|
||||
return new AbstractList<ResConfig>() {
|
||||
@Override
|
||||
public ResConfig get(int i) {
|
||||
TypeBlock typeBlock=TypeBlockArray.this.get(i);
|
||||
if(typeBlock!=null){
|
||||
return typeBlock.getResConfig();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return TypeBlockArray.this.childesCount();
|
||||
}
|
||||
};
|
||||
}
|
||||
private SpecBlock getSpecBlock(){
|
||||
Block parent=getParent();
|
||||
while(parent!=null){
|
||||
if(parent instanceof SpecBlock){
|
||||
return (SpecBlock)parent;
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public TypeBlock newInstance() {
|
||||
byte id=getTypeId();
|
||||
TypeBlock typeBlock=new TypeBlock();
|
||||
typeBlock.setTypeId(id);
|
||||
return typeBlock;
|
||||
}
|
||||
@Override
|
||||
public TypeBlock[] newInstance(int len) {
|
||||
return new TypeBlock[len];
|
||||
}
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
boolean readOk=true;
|
||||
while (readOk){
|
||||
readOk=readTypeBlockArray(reader);
|
||||
}
|
||||
}
|
||||
private boolean readTypeBlockArray(BlockReader reader) throws IOException{
|
||||
HeaderBlock headerBlock=reader.readHeaderBlock();
|
||||
if(headerBlock==null){
|
||||
return false;
|
||||
}
|
||||
ChunkType chunkType=headerBlock.getChunkType();
|
||||
if(chunkType!=ChunkType.TYPE){
|
||||
return false;
|
||||
}
|
||||
int pos=reader.getPosition();
|
||||
TypeBlock typeBlock=createNext();
|
||||
typeBlock.readBytes(reader);
|
||||
return reader.getPosition()>pos;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.reandroid.lib.arsc.array;
|
||||
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.TypeString;
|
||||
|
||||
public class TypeStringArray extends StringArray<TypeString> {
|
||||
public TypeStringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
|
||||
super(offsets, itemCount, itemStart, is_utf8);
|
||||
}
|
||||
@Override
|
||||
public TypeString newInstance() {
|
||||
return new TypeString(isUtf8());
|
||||
}
|
||||
@Override
|
||||
public TypeString[] newInstance(int len) {
|
||||
return new TypeString[len];
|
||||
}
|
||||
}
|
87
src/main/java/com/reandroid/lib/arsc/base/Block.java
Normal file
87
src/main/java/com/reandroid/lib/arsc/base/Block.java
Normal file
@ -0,0 +1,87 @@
|
||||
package com.reandroid.lib.arsc.base;
|
||||
|
||||
import com.reandroid.lib.arsc.io.BlockLoad;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public abstract class Block {
|
||||
private int mIndex;
|
||||
private Block mParent;
|
||||
private boolean mNull;
|
||||
private BlockLoad mBlockLoad;
|
||||
public abstract byte[] getBytes();
|
||||
public abstract int countBytes();
|
||||
public final int countUpTo(Block block){
|
||||
BlockCounter counter=new BlockCounter(block);
|
||||
onCountUpTo(counter);
|
||||
return counter.COUNT;
|
||||
}
|
||||
public abstract void onCountUpTo(BlockCounter counter);
|
||||
public final void readBytes(BlockReader reader) throws IOException{
|
||||
onReadBytes(reader);
|
||||
notifyBlockLoad(reader);
|
||||
}
|
||||
public final void setBlockLoad(BlockLoad blockLoad){
|
||||
mBlockLoad=blockLoad;
|
||||
}
|
||||
private void notifyBlockLoad(BlockReader reader) throws IOException{
|
||||
BlockLoad blockLoad=mBlockLoad;
|
||||
if(blockLoad!=null){
|
||||
blockLoad.onBlockLoaded(reader, this);
|
||||
}
|
||||
}
|
||||
public void onReadBytes(BlockReader reader) throws IOException{
|
||||
|
||||
}
|
||||
public final int writeBytes(OutputStream stream) throws IOException{
|
||||
if(isNull()){
|
||||
return 0;
|
||||
}
|
||||
return onWriteBytes(stream);
|
||||
}
|
||||
protected abstract int onWriteBytes(OutputStream stream) throws IOException;
|
||||
public boolean isNull(){
|
||||
return mNull;
|
||||
}
|
||||
public void setNull(boolean is_null){
|
||||
mNull=is_null;
|
||||
}
|
||||
public final int getIndex(){
|
||||
return mIndex;
|
||||
}
|
||||
public final void setIndex(int index){
|
||||
mIndex=index;
|
||||
}
|
||||
public final void setParent(Block parent){
|
||||
if(parent==this){
|
||||
return;
|
||||
}
|
||||
mParent=parent;
|
||||
}
|
||||
public final Block getParent(){
|
||||
return mParent;
|
||||
}
|
||||
|
||||
|
||||
protected static byte[] addBytes(byte[] bts1, byte[] bts2){
|
||||
boolean empty1=(bts1==null || bts1.length==0);
|
||||
boolean empty2=(bts2==null || bts2.length==0);
|
||||
if(empty1 && empty2){
|
||||
return null;
|
||||
}
|
||||
if(empty1){
|
||||
return bts2;
|
||||
}
|
||||
if(empty2){
|
||||
return bts1;
|
||||
}
|
||||
int len=bts1.length+bts2.length;
|
||||
byte[] result=new byte[len];
|
||||
int start=bts1.length;
|
||||
System.arraycopy(bts1, 0, result, 0, start);
|
||||
System.arraycopy(bts2, 0, result, start, bts2.length);
|
||||
return result;
|
||||
}
|
||||
}
|
220
src/main/java/com/reandroid/lib/arsc/base/BlockArray.java
Normal file
220
src/main/java/com/reandroid/lib/arsc/base/BlockArray.java
Normal file
@ -0,0 +1,220 @@
|
||||
package com.reandroid.lib.arsc.base;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public abstract class BlockArray<T extends Block> extends BlockContainer<T> implements BlockArrayCreator<T> {
|
||||
private T[] elementData;
|
||||
public BlockArray(){
|
||||
elementData= newInstance(0);
|
||||
}
|
||||
public List<T> listItems(){
|
||||
return new AbstractList<T>() {
|
||||
@Override
|
||||
public T get(int i) {
|
||||
return BlockArray.this.get(i);
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return BlockArray.this.childesCount();
|
||||
}
|
||||
};
|
||||
}
|
||||
@Override
|
||||
public T[] getChildes(){
|
||||
return elementData;
|
||||
}
|
||||
public void ensureSize(int size){
|
||||
if(size<= childesCount()){
|
||||
return;
|
||||
}
|
||||
setChildesCount(size);
|
||||
}
|
||||
public void setChildesCount(int count){
|
||||
if(count<0){
|
||||
count=0;
|
||||
}
|
||||
if(count==0){
|
||||
clearChildes();
|
||||
return;
|
||||
}
|
||||
int diff = count - childesCount();
|
||||
if(diff==0){
|
||||
return;
|
||||
}
|
||||
changeSize(diff);
|
||||
}
|
||||
public final void clearChildes(){
|
||||
T[] allChildes=elementData;
|
||||
if(allChildes==null || allChildes.length==0){
|
||||
return;
|
||||
}
|
||||
int max=allChildes.length;
|
||||
for(int i=0;i<max;i++){
|
||||
T block=allChildes[i];
|
||||
if(block==null){
|
||||
continue;
|
||||
}
|
||||
block.setIndex(-1);
|
||||
block.setParent(null);
|
||||
allChildes[i]=null;
|
||||
}
|
||||
elementData=newInstance(0);
|
||||
}
|
||||
|
||||
public void addAll(T[] blocks){
|
||||
if(blocks==null||blocks.length==0){
|
||||
return;
|
||||
}
|
||||
T[] old=elementData;
|
||||
if(old==null){
|
||||
old=newInstance(0);
|
||||
}
|
||||
int oldLen=old.length;
|
||||
int len=blocks.length;
|
||||
T[] update= newInstance(oldLen+len);
|
||||
if(oldLen>0){
|
||||
System.arraycopy(old, 0, update, 0, oldLen);
|
||||
}
|
||||
boolean foundNull=false;
|
||||
for(int i=0;i<len;i++){
|
||||
T item=blocks[i];
|
||||
if(item==null){
|
||||
foundNull=true;
|
||||
continue;
|
||||
}
|
||||
int index=oldLen + i;
|
||||
update[index]=item;
|
||||
item.setParent(this);
|
||||
item.setIndex(index);
|
||||
}
|
||||
elementData=update;
|
||||
if(foundNull){
|
||||
trimNullBlocks();
|
||||
}
|
||||
}
|
||||
public void add(T block){
|
||||
if(block==null){
|
||||
return;
|
||||
}
|
||||
T[] old=elementData;
|
||||
int index=old.length;
|
||||
elementData= newInstance(index+1);
|
||||
if(index>0){
|
||||
System.arraycopy(old, 0, elementData, 0, index);
|
||||
}
|
||||
elementData[index]=block;
|
||||
block.setIndex(index);
|
||||
block.setParent(this);
|
||||
}
|
||||
public final int childesCount(){
|
||||
return elementData.length;
|
||||
}
|
||||
public final T createNext(){
|
||||
T block=newInstance();
|
||||
add(block);
|
||||
return block;
|
||||
}
|
||||
public final T get(int i){
|
||||
if(i >= childesCount() || i<0){
|
||||
return null;
|
||||
}
|
||||
return elementData[i];
|
||||
}
|
||||
public boolean contains(T block){
|
||||
T[] items=elementData;
|
||||
if(block==null || items==null){
|
||||
return false;
|
||||
}
|
||||
int len=items.length;
|
||||
for(int i=0;i<len;i++){
|
||||
if(block==items[i]){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public boolean remove(T block){
|
||||
T[] items=elementData;
|
||||
if(block==null||items==null){
|
||||
return false;
|
||||
}
|
||||
boolean found=false;
|
||||
int len=items.length;
|
||||
for(int i=0;i<len;i++){
|
||||
if(block==items[i]){
|
||||
items[i]=null;
|
||||
found=true;
|
||||
}
|
||||
}
|
||||
if(found){
|
||||
trimNullBlocks();
|
||||
}
|
||||
return found;
|
||||
}
|
||||
private void trimNullBlocks(){
|
||||
T[] items=elementData;
|
||||
if(items==null){
|
||||
return;
|
||||
}
|
||||
int count=countNonNull();
|
||||
int len=items.length;
|
||||
if(count==len){
|
||||
return;
|
||||
}
|
||||
T[] update=newInstance(count);
|
||||
int index=0;
|
||||
for(int i=0;i<len;i++){
|
||||
T block=items[i];
|
||||
if(block!=null){
|
||||
update[index]=block;
|
||||
block.setIndex(index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
elementData=update;
|
||||
}
|
||||
private int countNonNull(){
|
||||
T[] items=elementData;
|
||||
if(items==null){
|
||||
return 0;
|
||||
}
|
||||
int result=0;
|
||||
for(T block:items){
|
||||
if(block!=null){
|
||||
result++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private void changeSize(int amount){
|
||||
T[] old=elementData;
|
||||
int index=old.length;
|
||||
int size=index+amount;
|
||||
T[] update= newInstance(size);
|
||||
int end;
|
||||
if(index>size){
|
||||
end=size;
|
||||
}else {
|
||||
end=index;
|
||||
}
|
||||
if(end>0){
|
||||
System.arraycopy(old, 0, update, 0, end);
|
||||
}
|
||||
for(int i=end;i<size;i++){
|
||||
T item=newInstance();
|
||||
update[i]=item;
|
||||
item.setIndex(i);
|
||||
item.setParent(this);
|
||||
}
|
||||
elementData=update;
|
||||
Arrays.fill(old, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "count="+ childesCount();
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.reandroid.lib.arsc.base;
|
||||
|
||||
public interface BlockArrayCreator<T extends Block> extends BlockCreator<T>{
|
||||
T[] newInstance(int len);
|
||||
}
|
132
src/main/java/com/reandroid/lib/arsc/base/BlockContainer.java
Normal file
132
src/main/java/com/reandroid/lib/arsc/base/BlockContainer.java
Normal file
@ -0,0 +1,132 @@
|
||||
package com.reandroid.lib.arsc.base;
|
||||
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public abstract class BlockContainer<T extends Block> extends Block{
|
||||
public BlockContainer(){
|
||||
super();
|
||||
}
|
||||
protected abstract void onRefreshed();
|
||||
public final void refresh(){
|
||||
if(isNull()){
|
||||
return;
|
||||
}
|
||||
refreshChildes();
|
||||
onRefreshed();
|
||||
}
|
||||
protected void refreshChildes(){
|
||||
T[] childes=getChildes();
|
||||
if(childes!=null){
|
||||
int max=childes.length;
|
||||
for(int i=0;i<max;i++){
|
||||
T item=childes[i];
|
||||
if(item instanceof BlockContainer){
|
||||
BlockContainer container=(BlockContainer)item;
|
||||
container.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onCountUpTo(BlockCounter counter){
|
||||
if(counter.FOUND){
|
||||
return;
|
||||
}
|
||||
if(counter.END==this){
|
||||
counter.FOUND=true;
|
||||
return;
|
||||
}
|
||||
T[] childes=getChildes();
|
||||
if(childes==null){
|
||||
return;
|
||||
}
|
||||
int max=childes.length;
|
||||
for(int i=0;i<max;i++){
|
||||
if(counter.FOUND){
|
||||
return;
|
||||
}
|
||||
T item=childes[i];
|
||||
if(item!=null){
|
||||
item.onCountUpTo(counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public int countBytes(){
|
||||
if(isNull()){
|
||||
return 0;
|
||||
}
|
||||
T[] childes=getChildes();
|
||||
if(childes==null){
|
||||
return 0;
|
||||
}
|
||||
int result=0;
|
||||
int max=childes.length;
|
||||
for(int i=0;i<max;i++){
|
||||
T item=childes[i];
|
||||
if(item!=null){
|
||||
result += item.countBytes();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public byte[] getBytes(){
|
||||
if(isNull()){
|
||||
return null;
|
||||
}
|
||||
T[] childes=getChildes();
|
||||
if(childes==null){
|
||||
return null;
|
||||
}
|
||||
byte[] results=null;
|
||||
int max=childes.length;
|
||||
for(int i=0;i<max;i++){
|
||||
T item=childes[i];
|
||||
if(item!=null){
|
||||
results = addBytes(results, item.getBytes());
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
@Override
|
||||
public int onWriteBytes(OutputStream stream) throws IOException {
|
||||
if(isNull()){
|
||||
return 0;
|
||||
}
|
||||
T[] childes=getChildes();
|
||||
if(childes==null){
|
||||
return 0;
|
||||
}
|
||||
int result=0;
|
||||
int max=childes.length;
|
||||
for(int i=0;i<max;i++){
|
||||
T item=childes[i];
|
||||
if(item!=null){
|
||||
result+=item.writeBytes(stream);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException{
|
||||
T[] childes=getChildes();
|
||||
if(childes==null){
|
||||
return;
|
||||
}
|
||||
int max=childes.length;
|
||||
for(int i=0;i<max;i++){
|
||||
T item=childes[i];
|
||||
if(item!=null){
|
||||
item.readBytes(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract int childesCount();
|
||||
public abstract T[] getChildes();
|
||||
}
|
16
src/main/java/com/reandroid/lib/arsc/base/BlockCounter.java
Normal file
16
src/main/java/com/reandroid/lib/arsc/base/BlockCounter.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.reandroid.lib.arsc.base;
|
||||
|
||||
public class BlockCounter {
|
||||
public final Block END;
|
||||
public boolean FOUND;
|
||||
int COUNT;
|
||||
BlockCounter(Block end){
|
||||
this.END=end;
|
||||
}
|
||||
public void addCount(int val){
|
||||
if(FOUND){
|
||||
return;
|
||||
}
|
||||
COUNT+=val;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.reandroid.lib.arsc.base;
|
||||
|
||||
public interface BlockCreator<T extends Block> {
|
||||
T newInstance();
|
||||
}
|
53
src/main/java/com/reandroid/lib/arsc/chunk/BaseChunk.java
Normal file
53
src/main/java/com/reandroid/lib/arsc/chunk/BaseChunk.java
Normal file
@ -0,0 +1,53 @@
|
||||
package com.reandroid.lib.arsc.chunk;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.container.ExpandableBlockContainer;
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class BaseChunk extends ExpandableBlockContainer {
|
||||
private final HeaderBlock mHeaderBlock;
|
||||
protected BaseChunk(short chunkType, int initialChildesCount) {
|
||||
super(initialChildesCount+1);
|
||||
mHeaderBlock=new HeaderBlock(chunkType);
|
||||
addChild(mHeaderBlock);
|
||||
}
|
||||
protected BaseChunk(ChunkType chunkType, int initialChildesCount) {
|
||||
this(chunkType.ID, initialChildesCount);
|
||||
}
|
||||
protected void addToHeader(Block block){
|
||||
mHeaderBlock.addChild(block);
|
||||
}
|
||||
public HeaderBlock getHeaderBlock(){
|
||||
return mHeaderBlock;
|
||||
}
|
||||
@Override
|
||||
protected final void onRefreshed() {
|
||||
mHeaderBlock.refreshHeader();
|
||||
onChunkRefreshed();
|
||||
}
|
||||
protected abstract void onChunkRefreshed();
|
||||
public void onChunkLoaded(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
HeaderBlock headerBlock=reader.readHeaderBlock();
|
||||
BlockReader chunkReader=reader.create(reader.getPosition(), headerBlock.getChunkSize());
|
||||
super.onReadBytes(chunkReader);
|
||||
reader.offset(headerBlock.getChunkSize());
|
||||
chunkReader.close();
|
||||
onChunkLoaded();
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(getClass().getSimpleName());
|
||||
builder.append(": ");
|
||||
builder.append(mHeaderBlock.toString());
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
116
src/main/java/com/reandroid/lib/arsc/chunk/BaseTypeBlock.java
Normal file
116
src/main/java/com/reandroid/lib/arsc/chunk/BaseTypeBlock.java
Normal file
@ -0,0 +1,116 @@
|
||||
package com.reandroid.lib.arsc.chunk;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.container.SpecTypePair;
|
||||
import com.reandroid.lib.arsc.item.ByteItem;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.TypeString;
|
||||
import com.reandroid.lib.arsc.pool.TypeStringPool;
|
||||
|
||||
abstract class BaseTypeBlock extends BaseChunk {
|
||||
private final ByteItem mTypeId;
|
||||
private final ByteItem mTypeFlags;
|
||||
private final ByteItem mReserved1;
|
||||
private final ByteItem mReserved2;
|
||||
private final IntegerItem mEntryCount;
|
||||
private TypeString mTypeString;
|
||||
BaseTypeBlock(ChunkType chunkType, int initialChildesCount) {
|
||||
super(chunkType, initialChildesCount);
|
||||
this.mTypeId=new ByteItem();
|
||||
this.mTypeFlags=new ByteItem();
|
||||
this.mReserved1=new ByteItem();
|
||||
this.mReserved2=new ByteItem();
|
||||
this.mEntryCount=new IntegerItem();
|
||||
addToHeader(mTypeId);
|
||||
addToHeader(mTypeFlags);
|
||||
addToHeader(mReserved1);
|
||||
addToHeader(mReserved2);
|
||||
addToHeader(mEntryCount);
|
||||
}
|
||||
public byte getTypeId(){
|
||||
return mTypeId.get();
|
||||
}
|
||||
public void setTypeId(byte id){
|
||||
mTypeId.set(id);
|
||||
}
|
||||
public void setEntryCount(int count){
|
||||
if(count == mEntryCount.get()){
|
||||
return;
|
||||
}
|
||||
mEntryCount.set(count);
|
||||
onSetEntryCount(count);
|
||||
}
|
||||
public int getEntryCount(){
|
||||
return mEntryCount.get();
|
||||
}
|
||||
public PackageBlock getPackageBlock(){
|
||||
Block parent=getParent();
|
||||
while (parent!=null){
|
||||
if(parent instanceof SpecTypePair){
|
||||
return ((SpecTypePair)parent).getPackageBlock();
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public TypeString getTypeString(){
|
||||
if(mTypeString!=null){
|
||||
if(mTypeString.getId()==getTypeId()){
|
||||
return mTypeString;
|
||||
}
|
||||
mTypeString=null;
|
||||
}
|
||||
PackageBlock packageBlock=getPackageBlock();
|
||||
if(packageBlock==null){
|
||||
return null;
|
||||
}
|
||||
TypeStringPool typeStringPool=packageBlock.getTypeStringPool();
|
||||
mTypeString=typeStringPool.getById(getTypeId());
|
||||
return mTypeString;
|
||||
}
|
||||
SpecTypePair getSpecTypePair(){
|
||||
Block parent=getParent();
|
||||
while (parent!=null){
|
||||
if(parent instanceof SpecTypePair){
|
||||
return (SpecTypePair)parent;
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
abstract void onSetEntryCount(int count);
|
||||
IntegerItem getEntryCountBlock(){
|
||||
return mEntryCount;
|
||||
}
|
||||
private TypeStringPool getTypeStringPool(){
|
||||
PackageBlock packageBlock=getPackageBlock();
|
||||
if(packageBlock!=null){
|
||||
return packageBlock.getTypeStringPool();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(getClass().getSimpleName());
|
||||
builder.append(": ");
|
||||
PackageBlock packageBlock=getPackageBlock();
|
||||
if(packageBlock!=null){
|
||||
builder.append("PKG=");
|
||||
builder.append(String.format("0x%02x", packageBlock.getId()));
|
||||
builder.append(" ");
|
||||
}
|
||||
builder.append(getHeaderBlock().toString());
|
||||
builder.append(" entries=");
|
||||
builder.append(getEntryCount());
|
||||
builder.append(", id=");
|
||||
builder.append(String.format("0x%02x", getTypeId()));
|
||||
TypeString typeString=getTypeString();
|
||||
if(typeString!=null){
|
||||
builder.append('(');
|
||||
builder.append(typeString.get());
|
||||
builder.append(')');
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
75
src/main/java/com/reandroid/lib/arsc/chunk/ChunkType.java
Normal file
75
src/main/java/com/reandroid/lib/arsc/chunk/ChunkType.java
Normal file
@ -0,0 +1,75 @@
|
||||
package com.reandroid.lib.arsc.chunk;
|
||||
|
||||
public enum ChunkType {
|
||||
NULL((short)0x0000),
|
||||
|
||||
STRING((short)0x0001),
|
||||
TABLE((short)0x0002),
|
||||
XML((short)0x0003),
|
||||
|
||||
XML_START_NAMESPACE((short)0x0100),
|
||||
XML_END_NAMESPACE((short)0x0101),
|
||||
XML_START_ELEMENT((short)0x0102),
|
||||
XML_END_ELEMENT((short)0x0103),
|
||||
XML_CDATA((short)0x0104),
|
||||
XML_LAST_CHUNK((short)0x017f),
|
||||
XML_RESOURCE_MAP((short)0x0180),
|
||||
|
||||
PACKAGE((short)0x0200),
|
||||
TYPE((short)0x0201),
|
||||
SPEC((short)0x0202),
|
||||
LIBRARY((short)0x0203);
|
||||
|
||||
public final short ID;
|
||||
ChunkType(short id) {
|
||||
this.ID = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return name()+String.format("(0x%04x)", ((int) ID));
|
||||
}
|
||||
|
||||
public static ChunkType get(short id){
|
||||
ChunkType[] all=values();
|
||||
for(ChunkType t:all){
|
||||
if(t.ID ==id){
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ChunkType getTable(short id){
|
||||
for(ChunkType t:table_chunk_types){
|
||||
if(t.ID ==id){
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static ChunkType getXml(short id){
|
||||
for(ChunkType t:xml_chunk_types){
|
||||
if(t.ID ==id){
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final ChunkType[] table_chunk_types=new ChunkType[]{
|
||||
PACKAGE,
|
||||
TYPE,
|
||||
SPEC,
|
||||
LIBRARY
|
||||
};
|
||||
private static final ChunkType[] xml_chunk_types=new ChunkType[]{
|
||||
XML_START_NAMESPACE,
|
||||
XML_END_NAMESPACE,
|
||||
XML_START_ELEMENT,
|
||||
XML_END_ELEMENT,
|
||||
XML_CDATA,
|
||||
XML_LAST_CHUNK,
|
||||
XML_RESOURCE_MAP
|
||||
};
|
||||
}
|
57
src/main/java/com/reandroid/lib/arsc/chunk/LibraryBlock.java
Normal file
57
src/main/java/com/reandroid/lib/arsc/chunk/LibraryBlock.java
Normal file
@ -0,0 +1,57 @@
|
||||
package com.reandroid.lib.arsc.chunk;
|
||||
|
||||
import com.reandroid.lib.arsc.array.LibraryInfoArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.value.LibraryInfo;
|
||||
|
||||
public class LibraryBlock extends BaseChunk {
|
||||
private final IntegerItem mLibCount;
|
||||
private final LibraryInfoArray mLibraryInfoArray;
|
||||
public LibraryBlock() {
|
||||
super(ChunkType.LIBRARY,1);
|
||||
this.mLibCount=new IntegerItem();
|
||||
this.mLibraryInfoArray=new LibraryInfoArray(mLibCount);
|
||||
|
||||
addToHeader(mLibCount);
|
||||
addChild(mLibraryInfoArray);
|
||||
}
|
||||
public LibraryInfo[] getAllInfo(){
|
||||
return mLibraryInfoArray.getChildes();
|
||||
}
|
||||
public void addLibraryInfo(LibraryBlock libraryBlock){
|
||||
if(libraryBlock==null){
|
||||
return;
|
||||
}
|
||||
LibraryInfo[] allInfo=libraryBlock.getAllInfo();
|
||||
if (allInfo==null){
|
||||
return;
|
||||
}
|
||||
for(LibraryInfo info:allInfo){
|
||||
addLibraryInfo(info);
|
||||
}
|
||||
}
|
||||
public void addLibraryInfo(LibraryInfo info){
|
||||
if(info==null){
|
||||
return;
|
||||
}
|
||||
mLibraryInfoArray.add(info);
|
||||
mLibCount.set(mLibraryInfoArray.childesCount());
|
||||
}
|
||||
@Override
|
||||
public boolean isNull(){
|
||||
return mLibraryInfoArray.childesCount()==0;
|
||||
}
|
||||
public int getLibraryCount(){
|
||||
return mLibraryInfoArray.childesCount();
|
||||
}
|
||||
public void setLibraryCount(int count){
|
||||
mLibCount.set(count);
|
||||
mLibraryInfoArray.setChildesCount(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChunkRefreshed() {
|
||||
mLibCount.set(mLibraryInfoArray.childesCount());
|
||||
}
|
||||
|
||||
}
|
137
src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java
Normal file
137
src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java
Normal file
@ -0,0 +1,137 @@
|
||||
package com.reandroid.lib.arsc.chunk;
|
||||
|
||||
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.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.PackageName;
|
||||
import com.reandroid.lib.arsc.pool.SpecStringPool;
|
||||
import com.reandroid.lib.arsc.pool.TypeStringPool;
|
||||
import com.reandroid.lib.arsc.value.LibraryInfo;
|
||||
|
||||
|
||||
public class PackageBlock extends BaseChunk {
|
||||
private final IntegerItem mPackageId;
|
||||
private final PackageName mPackageName;
|
||||
|
||||
private final IntegerItem mTypeStrings;
|
||||
private final IntegerItem mLastPublicType;
|
||||
private final IntegerItem mKeyStrings;
|
||||
private final IntegerItem mLastPublicKey;
|
||||
private final IntegerItem mTypeIdOffset;
|
||||
|
||||
private final TypeStringPool mTypeStringPool;
|
||||
private final SpecStringPool mSpecStringPool;
|
||||
|
||||
private final SpecTypePairArray mSpecTypePairArray;
|
||||
private final LibraryBlock mLibraryBlock;
|
||||
|
||||
private final PackageLastBlocks mPackageLastBlocks;
|
||||
|
||||
public PackageBlock() {
|
||||
super(ChunkType.PACKAGE, 3);
|
||||
this.mPackageId=new IntegerItem();
|
||||
this.mPackageName=new PackageName();
|
||||
this.mTypeStrings=new IntegerItem();
|
||||
this.mLastPublicType=new IntegerItem();
|
||||
this.mKeyStrings=new IntegerItem();
|
||||
this.mLastPublicKey=new IntegerItem();
|
||||
this.mTypeIdOffset=new IntegerItem();
|
||||
|
||||
this.mTypeStringPool=new TypeStringPool(false);
|
||||
this.mSpecStringPool=new SpecStringPool(true);
|
||||
|
||||
this.mSpecTypePairArray=new SpecTypePairArray();
|
||||
this.mLibraryBlock=new LibraryBlock();
|
||||
this.mPackageLastBlocks=new PackageLastBlocks(mSpecTypePairArray, mLibraryBlock);
|
||||
|
||||
addToHeader(mPackageId);
|
||||
addToHeader(mPackageName);
|
||||
addToHeader(mTypeStrings);
|
||||
addToHeader(mLastPublicType);
|
||||
addToHeader(mKeyStrings);
|
||||
addToHeader(mLastPublicKey);
|
||||
addToHeader(mTypeIdOffset);
|
||||
|
||||
addChild(mTypeStringPool);
|
||||
addChild(mSpecStringPool);
|
||||
|
||||
addChild(mPackageLastBlocks);
|
||||
|
||||
}
|
||||
public int getId(){
|
||||
return mPackageId.get();
|
||||
}
|
||||
public void setId(int id){
|
||||
mPackageId.set(id);
|
||||
}
|
||||
public String getName(){
|
||||
return mPackageName.get();
|
||||
}
|
||||
public void setName(String name){
|
||||
mPackageName.set(name);
|
||||
}
|
||||
|
||||
|
||||
public TableBlock getTableBlock(){
|
||||
Block parent=getParent();
|
||||
while(parent!=null){
|
||||
if(parent instanceof TableBlock){
|
||||
return (TableBlock)parent;
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public TypeStringPool getTypeStringPool(){
|
||||
return mTypeStringPool;
|
||||
}
|
||||
public SpecStringPool getSpecStringPool(){
|
||||
return mSpecStringPool;
|
||||
}
|
||||
|
||||
public void addLibrary(LibraryBlock libraryBlock){
|
||||
if(libraryBlock==null){
|
||||
return;
|
||||
}
|
||||
LibraryInfo[] allInfo=libraryBlock.getAllInfo();
|
||||
if (allInfo==null){
|
||||
return;
|
||||
}
|
||||
for(LibraryInfo info:allInfo){
|
||||
addLibraryInfo(info);
|
||||
}
|
||||
}
|
||||
public void addLibraryInfo(LibraryInfo info){
|
||||
mLibraryBlock.addLibraryInfo(info);
|
||||
}
|
||||
|
||||
private void refreshKeyStrings(){
|
||||
int pos=countUpTo(mSpecStringPool);
|
||||
mKeyStrings.set(pos);
|
||||
}
|
||||
@Override
|
||||
public void onChunkLoaded() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChunkRefreshed() {
|
||||
refreshKeyStrings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(super.toString());
|
||||
builder.append(", id=");
|
||||
builder.append(String.format("0x%02x", getId()));
|
||||
builder.append(", name=");
|
||||
builder.append(getName());
|
||||
int libCount=mLibraryBlock.getLibraryCount();
|
||||
if(libCount>0){
|
||||
builder.append(", libraries=");
|
||||
builder.append(libCount);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
54
src/main/java/com/reandroid/lib/arsc/chunk/SpecBlock.java
Normal file
54
src/main/java/com/reandroid/lib/arsc/chunk/SpecBlock.java
Normal file
@ -0,0 +1,54 @@
|
||||
package com.reandroid.lib.arsc.chunk;
|
||||
|
||||
import com.reandroid.lib.arsc.array.TypeBlockArray;
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.container.SpecTypePair;
|
||||
import com.reandroid.lib.arsc.io.BlockLoad;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SpecBlock extends BaseTypeBlock implements BlockLoad {
|
||||
private final IntegerArray mOffsets;
|
||||
public SpecBlock() {
|
||||
super(ChunkType.SPEC, 1);
|
||||
this.mOffsets=new IntegerArray();
|
||||
addChild(mOffsets);
|
||||
|
||||
getEntryCountBlock().setBlockLoad(this);
|
||||
}
|
||||
public TypeBlockArray getTypeBlockArray(){
|
||||
SpecTypePair specTypePair=getSpecTypePair();
|
||||
if(specTypePair!=null){
|
||||
return specTypePair.getTypeBlockArray();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
void onSetEntryCount(int count) {
|
||||
mOffsets.setSize(count);
|
||||
}
|
||||
@Override
|
||||
protected void onChunkRefreshed() {
|
||||
}
|
||||
@Override
|
||||
public void onBlockLoaded(BlockReader reader, Block sender) throws IOException {
|
||||
IntegerItem entryCount=getEntryCountBlock();
|
||||
if(sender==entryCount){
|
||||
mOffsets.setSize(entryCount.get());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(super.toString());
|
||||
TypeBlockArray typeBlockArray=getTypeBlockArray();
|
||||
if(typeBlockArray!=null){
|
||||
builder.append(", typesCount=");
|
||||
builder.append(typeBlockArray.childesCount());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
40
src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java
Normal file
40
src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java
Normal file
@ -0,0 +1,40 @@
|
||||
package com.reandroid.lib.arsc.chunk;
|
||||
|
||||
import com.reandroid.lib.arsc.array.PackageArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.pool.TableStringPool;
|
||||
|
||||
public class TableBlock extends BaseChunk {
|
||||
private final IntegerItem mPackageCount;
|
||||
private final TableStringPool mTableStringPool;
|
||||
private final PackageArray mPackageArray;
|
||||
public TableBlock() {
|
||||
super(ChunkType.TABLE, 2);
|
||||
this.mPackageCount=new IntegerItem();
|
||||
this.mTableStringPool=new TableStringPool(true);
|
||||
this.mPackageArray=new PackageArray(mPackageCount);
|
||||
addToHeader(mPackageCount);
|
||||
addChild(mTableStringPool);
|
||||
addChild(mPackageArray);
|
||||
}
|
||||
public TableStringPool getTableStringPool(){
|
||||
return mTableStringPool;
|
||||
}
|
||||
public PackageArray getPackageArray(){
|
||||
return mPackageArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChunkRefreshed() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(super.toString());
|
||||
builder.append(", packages=");
|
||||
int pkgCount=mPackageArray.childesCount();
|
||||
builder.append(pkgCount);
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
48
src/main/java/com/reandroid/lib/arsc/chunk/TypeBlock.java
Normal file
48
src/main/java/com/reandroid/lib/arsc/chunk/TypeBlock.java
Normal file
@ -0,0 +1,48 @@
|
||||
package com.reandroid.lib.arsc.chunk;
|
||||
|
||||
import com.reandroid.lib.arsc.array.EntryBlockArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.value.ResConfig;
|
||||
|
||||
public class TypeBlock extends BaseTypeBlock {
|
||||
private final IntegerItem mEntriesStart;
|
||||
private final ResConfig mResConfig;
|
||||
private final IntegerArray mEntryOffsets;
|
||||
private final EntryBlockArray mEntryArray;
|
||||
public TypeBlock() {
|
||||
super(ChunkType.TYPE, 2);
|
||||
this.mEntriesStart=new IntegerItem();
|
||||
this.mResConfig =new ResConfig();
|
||||
this.mEntryOffsets=new IntegerArray();
|
||||
this.mEntryArray=new EntryBlockArray(mEntryOffsets, getEntryCountBlock(), mEntriesStart);
|
||||
|
||||
addToHeader(mEntriesStart);
|
||||
addToHeader(mResConfig);
|
||||
|
||||
addChild(mEntryOffsets);
|
||||
addChild(mEntryArray);
|
||||
}
|
||||
public ResConfig getResConfig(){
|
||||
return mResConfig;
|
||||
}
|
||||
public EntryBlockArray getEntryBlockArray(){
|
||||
return mEntryArray;
|
||||
}
|
||||
@Override
|
||||
void onSetEntryCount(int count) {
|
||||
mEntryArray.setChildesCount(count);
|
||||
}
|
||||
@Override
|
||||
protected void onChunkRefreshed() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(super.toString());
|
||||
builder.append(", config=");
|
||||
builder.append(getResConfig().toString());
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
132
src/main/java/com/reandroid/lib/arsc/chunk/xml/BaseXmlChunk.java
Normal file
132
src/main/java/com/reandroid/lib/arsc/chunk/xml/BaseXmlChunk.java
Normal file
@ -0,0 +1,132 @@
|
||||
package com.reandroid.lib.arsc.chunk.xml;
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.chunk.BaseChunk;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.ResXmlString;
|
||||
import com.reandroid.lib.arsc.pool.ResXmlStringPool;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class BaseXmlChunk extends BaseChunk {
|
||||
|
||||
private final IntegerItem mLineNumber;
|
||||
private final IntegerItem mCommentReference;
|
||||
private final IntegerItem mNamespaceReference;
|
||||
private final IntegerItem mStringReference;
|
||||
BaseXmlChunk(ChunkType chunkType, int initialChildesCount) {
|
||||
super(chunkType, initialChildesCount+2);
|
||||
this.mLineNumber=new IntegerItem();
|
||||
this.mCommentReference =new IntegerItem(-1);
|
||||
|
||||
this.mNamespaceReference=new IntegerItem(-1);
|
||||
this.mStringReference=new IntegerItem(-1);
|
||||
|
||||
addToHeader(mLineNumber);
|
||||
addToHeader(mCommentReference);
|
||||
|
||||
addChild(mNamespaceReference);
|
||||
addChild(mStringReference);
|
||||
}
|
||||
|
||||
|
||||
public void setLineNumber(int val){
|
||||
mLineNumber.set(val);
|
||||
}
|
||||
public int getLineNumber(){
|
||||
return mLineNumber.get();
|
||||
}
|
||||
public void setCommentReference(int val){
|
||||
mLineNumber.set(val);
|
||||
}
|
||||
public int getCommentReference(){
|
||||
return mCommentReference.get();
|
||||
}
|
||||
public void setNamespaceReference(int val){
|
||||
mNamespaceReference.set(val);
|
||||
}
|
||||
public int getNamespaceReference(){
|
||||
return mNamespaceReference.get();
|
||||
}
|
||||
public void setStringReference(int val){
|
||||
mStringReference.set(val);
|
||||
}
|
||||
public int getStringReference(){
|
||||
return mStringReference.get();
|
||||
}
|
||||
|
||||
|
||||
public ResXmlStringPool getStringPool(){
|
||||
Block parent=getParent();
|
||||
while (parent!=null){
|
||||
if(parent instanceof ResXmlBlock){
|
||||
return ((ResXmlBlock)parent).getStringPool();
|
||||
}
|
||||
if(parent instanceof ResXmlElement){
|
||||
return ((ResXmlElement)parent).getStringPool();
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public ResXmlString getResXmlString(int ref){
|
||||
if(ref<0){
|
||||
return null;
|
||||
}
|
||||
ResXmlStringPool stringPool=getStringPool();
|
||||
if(stringPool!=null){
|
||||
return stringPool.get(ref);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
String getString(int ref){
|
||||
ResXmlString xmlString=getResXmlString(ref);
|
||||
if(xmlString!=null){
|
||||
return xmlString.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getName(){
|
||||
return getString(getStringReference());
|
||||
}
|
||||
public String getUri(){
|
||||
return getString(getNamespaceReference());
|
||||
}
|
||||
public ResXmlElement getParentResXmlElement(){
|
||||
Block parent=getParent();
|
||||
while (parent!=null){
|
||||
if(parent instanceof ResXmlElement){
|
||||
return (ResXmlElement)parent;
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
super.onReadBytes(reader);
|
||||
}
|
||||
@Override
|
||||
protected void onChunkRefreshed() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
ChunkType chunkType=getHeaderBlock().getChunkType();
|
||||
if(chunkType==null){
|
||||
return super.toString();
|
||||
}
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(chunkType.toString());
|
||||
builder.append(": line=");
|
||||
builder.append(getLineNumber());
|
||||
builder.append(" {");
|
||||
builder.append(getName());
|
||||
builder.append("}");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,192 @@
|
||||
package com.reandroid.lib.arsc.chunk.xml;
|
||||
|
||||
import com.reandroid.lib.arsc.array.ResXmlIDArray;
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.container.FixedBlockContainer;
|
||||
import com.reandroid.lib.arsc.item.*;
|
||||
import com.reandroid.lib.arsc.pool.ResXmlStringPool;
|
||||
import com.reandroid.lib.arsc.value.ValueType;
|
||||
|
||||
public class ResXmlAttribute extends FixedBlockContainer {
|
||||
private final IntegerItem mNamespaceReference;
|
||||
private final IntegerItem mNameReference;
|
||||
private final IntegerItem mValueStringReference;
|
||||
private final ShortItem mNameType;
|
||||
private final ByteItem mReserved;
|
||||
private final ByteItem mValueTypeByte;
|
||||
private final IntegerItem mRawValue;
|
||||
public ResXmlAttribute() {
|
||||
super(7);
|
||||
mNamespaceReference =new IntegerItem();
|
||||
mNameReference =new IntegerItem();
|
||||
mValueStringReference =new IntegerItem();
|
||||
mNameType=new ShortItem();
|
||||
mReserved =new ByteItem();
|
||||
mValueTypeByte=new ByteItem();
|
||||
mRawValue=new IntegerItem();
|
||||
addChild(0, mNamespaceReference);
|
||||
addChild(1, mNameReference);
|
||||
addChild(2, mValueStringReference);
|
||||
addChild(3, mNameType);
|
||||
addChild(4, mReserved);
|
||||
addChild(5, mValueTypeByte);
|
||||
addChild(6, mRawValue);
|
||||
}
|
||||
public int getNamespaceReference(){
|
||||
return mNamespaceReference.get();
|
||||
}
|
||||
public void setNamespaceReference(int ref){
|
||||
mNamespaceReference.set(ref);
|
||||
}
|
||||
public int getNameReference(){
|
||||
return mNameReference.get();
|
||||
}
|
||||
public void setNameReference(int ref){
|
||||
mNameReference.set(ref);
|
||||
}
|
||||
public int getValueStringReference(){
|
||||
return mValueStringReference.get();
|
||||
}
|
||||
public void setValueStringReference(int ref){
|
||||
mValueStringReference.set(ref);
|
||||
}
|
||||
public short getNameType(){
|
||||
return mNameType.get();
|
||||
}
|
||||
public void setNameType(short s){
|
||||
mNameType.set(s);
|
||||
}
|
||||
public byte getValueTypeByte(){
|
||||
return mValueTypeByte.get();
|
||||
}
|
||||
public void setValueTypeByte(byte b){
|
||||
mValueTypeByte.set(b);
|
||||
}
|
||||
public int getRawValue(){
|
||||
return mRawValue.get();
|
||||
}
|
||||
public void setRawValue(int val){
|
||||
mRawValue.set(val);
|
||||
}
|
||||
|
||||
public ValueType getValueType(){
|
||||
return ValueType.valueOf(getValueTypeByte());
|
||||
}
|
||||
public String getFullName(){
|
||||
String name=getName();
|
||||
if(name==null){
|
||||
return null;
|
||||
}
|
||||
String prefix=getNamePrefix();
|
||||
if(prefix==null){
|
||||
return name;
|
||||
}
|
||||
return prefix+":"+name;
|
||||
}
|
||||
public String getName(){
|
||||
return getString(getNameReference());
|
||||
}
|
||||
public String getNamePrefix(){
|
||||
ResXmlElement xmlElement=getParentResXmlElement();
|
||||
if(xmlElement==null){
|
||||
return null;
|
||||
}
|
||||
ResXmlStartNamespace startNamespace=xmlElement.getStartNamespaceByUriRef(getNamespaceReference());
|
||||
if(startNamespace==null){
|
||||
return null;
|
||||
}
|
||||
return startNamespace.getPrefix();
|
||||
}
|
||||
public String getValueString(){
|
||||
return getString(getValueStringReference());
|
||||
}
|
||||
public int getNameResourceID(){
|
||||
return getResourceId(getNameReference());
|
||||
}
|
||||
private int getResourceId(int ref){
|
||||
if(ref<=0){
|
||||
return 0;
|
||||
}
|
||||
ResXmlIDMap xmlIDMap=getResXmlIDMap();
|
||||
if(xmlIDMap==null){
|
||||
return 0;
|
||||
}
|
||||
ResXmlIDArray xmlIDArray = xmlIDMap.getResXmlIDArray();
|
||||
ResXmlID xmlID = xmlIDArray.get(ref);
|
||||
if(xmlID!=null){
|
||||
return xmlID.get();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private String getString(int ref){
|
||||
if(ref<0){
|
||||
return null;
|
||||
}
|
||||
ResXmlString xmlString=getResXmlString(ref);
|
||||
if(xmlString!=null){
|
||||
return xmlString.getHtml();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private ResXmlString getResXmlString(int ref){
|
||||
ResXmlStringPool stringPool=getStringPool();
|
||||
if(stringPool!=null){
|
||||
return stringPool.get(ref);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private ResXmlStringPool getStringPool(){
|
||||
ResXmlElement xmlElement=getParentResXmlElement();
|
||||
if(xmlElement!=null){
|
||||
return xmlElement.getStringPool();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private ResXmlIDMap getResXmlIDMap(){
|
||||
ResXmlElement xmlElement=getParentResXmlElement();
|
||||
if(xmlElement!=null){
|
||||
return xmlElement.getResXmlIDMap();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private ResXmlElement getParentResXmlElement(){
|
||||
Block parent=getParent();
|
||||
while (parent!=null){
|
||||
if(parent instanceof ResXmlElement){
|
||||
return (ResXmlElement)parent;
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
String fullName=getFullName();
|
||||
if(fullName!=null ){
|
||||
int id=getNameResourceID();
|
||||
if(id>0){
|
||||
fullName=fullName+"(@"+String.format("0x%08x",id)+")";
|
||||
}
|
||||
String valStr=getValueString();
|
||||
if(valStr!=null){
|
||||
return getIndex()+" {"+fullName+"=\""+valStr+"\""+"}";
|
||||
}
|
||||
return getIndex()+" {"+fullName+"}"+"["+getValueType()+"]=\""+getRawValue()+"\"";
|
||||
}
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(getClass().getSimpleName());
|
||||
builder.append(": ");
|
||||
builder.append(getIndex());
|
||||
builder.append("{NamespaceReference=").append(getNamespaceReference());
|
||||
builder.append(", NameReference=").append(getNameReference());
|
||||
builder.append(", ValueStringReference=").append(getValueStringReference());
|
||||
builder.append(", NameType=").append(getNameType());
|
||||
builder.append(", ReservedByte=").append(mReserved.get());
|
||||
builder.append(", ValueTypeByte=").append(getValueTypeByte());
|
||||
builder.append(", RawValue=").append(getRawValue());
|
||||
builder.append("}");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
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.pool.ResXmlStringPool;
|
||||
|
||||
public class ResXmlBlock extends BaseChunk {
|
||||
private final ResXmlStringPool mResXmlStringPool;
|
||||
private final ResXmlIDMap mResXmlIDMap;
|
||||
private final ResXmlElement mResXmlElement;
|
||||
public ResXmlBlock() {
|
||||
super(ChunkType.XML,3);
|
||||
this.mResXmlStringPool=new ResXmlStringPool(true);
|
||||
this.mResXmlIDMap=new ResXmlIDMap();
|
||||
this.mResXmlElement=new ResXmlElement();
|
||||
addChild(mResXmlStringPool);
|
||||
addChild(mResXmlIDMap);
|
||||
addChild(mResXmlElement);
|
||||
}
|
||||
public ResXmlStringPool getStringPool(){
|
||||
return mResXmlStringPool;
|
||||
}
|
||||
public ResXmlIDMap getResXmlIDMap(){
|
||||
return mResXmlIDMap;
|
||||
}
|
||||
public ResXmlElement getResXmlElement(){
|
||||
return mResXmlElement;
|
||||
}
|
||||
@Override
|
||||
protected void onChunkRefreshed() {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,290 @@
|
||||
package com.reandroid.lib.arsc.chunk.xml;
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.container.BlockList;
|
||||
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.pool.ResXmlStringPool;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class ResXmlElement extends FixedBlockContainer {
|
||||
private final BlockList<ResXmlStartNamespace> mStartNamespaceList;
|
||||
private final SingleBlockContainer<ResXmlStartElement> mStartElementContainer;
|
||||
private final BlockList<ResXmlElement> mBody;
|
||||
private final SingleBlockContainer<ResXmlText> mResXmlTextContainer;
|
||||
private final SingleBlockContainer<ResXmlEndElement> mEndElementContainer;
|
||||
private final BlockList<ResXmlEndNamespace> mEndNamespaceList;
|
||||
private int mDepth;
|
||||
public ResXmlElement() {
|
||||
super(6);
|
||||
this.mStartNamespaceList =new BlockList<>();
|
||||
this.mStartElementContainer=new SingleBlockContainer<>();
|
||||
this.mBody=new BlockList<>();
|
||||
this.mResXmlTextContainer=new SingleBlockContainer<>();
|
||||
this.mEndElementContainer=new SingleBlockContainer<>();
|
||||
this.mEndNamespaceList =new BlockList<>();
|
||||
addChild(0, mStartNamespaceList);
|
||||
addChild(1, mStartElementContainer);
|
||||
addChild(2, mBody);
|
||||
addChild(3, mResXmlTextContainer);
|
||||
addChild(4, mEndElementContainer);
|
||||
addChild(5, mEndNamespaceList);
|
||||
}
|
||||
public ResXmlStringPool getStringPool(){
|
||||
Block parent=getParent();
|
||||
while (parent!=null){
|
||||
if(parent instanceof ResXmlBlock){
|
||||
return ((ResXmlBlock)parent).getStringPool();
|
||||
}
|
||||
if(parent instanceof ResXmlElement){
|
||||
return ((ResXmlElement)parent).getStringPool();
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public ResXmlIDMap getResXmlIDMap(){
|
||||
Block parent=getParent();
|
||||
while (parent!=null){
|
||||
if(parent instanceof ResXmlBlock){
|
||||
return ((ResXmlBlock)parent).getResXmlIDMap();
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getDepth(){
|
||||
return mDepth;
|
||||
}
|
||||
private void setDepth(int depth){
|
||||
mDepth=depth;
|
||||
}
|
||||
public void addElement(ResXmlElement element){
|
||||
mBody.add(element);
|
||||
}
|
||||
public int countElements(){
|
||||
return mBody.size();
|
||||
}
|
||||
public List<ResXmlElement> listElements(){
|
||||
return mBody.getChildes();
|
||||
}
|
||||
public ResXmlElement getParentResXmlElement(){
|
||||
Block parent=getParent();
|
||||
while (parent!=null){
|
||||
if(parent instanceof ResXmlElement){
|
||||
return (ResXmlElement)parent;
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public ResXmlStartNamespace getStartNamespaceByUriRef(int uriRef){
|
||||
if(uriRef<0){
|
||||
return null;
|
||||
}
|
||||
for(ResXmlStartNamespace ns:mStartNamespaceList.getChildes()){
|
||||
if(uriRef==ns.getUriReference()){
|
||||
return ns;
|
||||
}
|
||||
}
|
||||
ResXmlElement xmlElement=getParentResXmlElement();
|
||||
if(xmlElement!=null){
|
||||
return xmlElement.getStartNamespaceByUriRef(uriRef);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public List<ResXmlStartNamespace> getStartNamespaceList(){
|
||||
return mStartNamespaceList.getChildes();
|
||||
}
|
||||
public void addStartNamespace(ResXmlStartNamespace item){
|
||||
mStartNamespaceList.add(item);
|
||||
}
|
||||
|
||||
public List<ResXmlEndNamespace> getEndNamespaceList(){
|
||||
return mEndNamespaceList.getChildes();
|
||||
}
|
||||
public void addEndNamespace(ResXmlEndNamespace item){
|
||||
mEndNamespaceList.add(item);
|
||||
}
|
||||
|
||||
public ResXmlStartElement getStartElement(){
|
||||
return mStartElementContainer.getItem();
|
||||
}
|
||||
public void setStartElement(ResXmlStartElement item){
|
||||
mStartElementContainer.setItem(item);
|
||||
}
|
||||
|
||||
public ResXmlEndElement getEndElement(){
|
||||
return mEndElementContainer.getItem();
|
||||
}
|
||||
public void setEndElement(ResXmlEndElement item){
|
||||
mEndElementContainer.setItem(item);
|
||||
}
|
||||
|
||||
public ResXmlText getResXmlText(){
|
||||
return mResXmlTextContainer.getItem();
|
||||
}
|
||||
public void setResXmlText(ResXmlText xmlText){
|
||||
mResXmlTextContainer.setItem(xmlText);
|
||||
}
|
||||
|
||||
private boolean isBalanced(){
|
||||
return isElementBalanced() && isNamespaceBalanced();
|
||||
}
|
||||
private boolean isNamespaceBalanced(){
|
||||
return (mStartNamespaceList.size()==mEndNamespaceList.size());
|
||||
}
|
||||
private boolean isElementBalanced(){
|
||||
return (hasStartElement() && hasEndElement());
|
||||
}
|
||||
private boolean hasStartElement(){
|
||||
return mStartElementContainer.hasItem();
|
||||
}
|
||||
private boolean hasEndElement(){
|
||||
return mEndElementContainer.hasItem();
|
||||
}
|
||||
|
||||
private void linkStartEnd(){
|
||||
linkStartEndElement();
|
||||
linkStartEndNameSpaces();
|
||||
}
|
||||
private void linkStartEndElement(){
|
||||
ResXmlStartElement start=getStartElement();
|
||||
ResXmlEndElement end=getEndElement();
|
||||
if(start==null || end==null){
|
||||
return;
|
||||
}
|
||||
start.setResXmlEndElement(end);
|
||||
end.setResXmlStartElement(start);
|
||||
}
|
||||
private void linkStartEndNameSpaces(){
|
||||
if(!isNamespaceBalanced()){
|
||||
return;
|
||||
}
|
||||
int max=mStartNamespaceList.size();
|
||||
for(int i=0;i<max;i++){
|
||||
ResXmlStartNamespace start=mStartNamespaceList.get(i);
|
||||
ResXmlEndNamespace end=mEndNamespaceList.get(max-i-1);
|
||||
start.setResXmlEndNamespace(end);
|
||||
end.setResXmlStartNamespace(start);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
if(isBalanced()){
|
||||
return;
|
||||
}
|
||||
HeaderBlock headerBlock=reader.readHeaderBlock();
|
||||
if(headerBlock==null){
|
||||
return;
|
||||
}
|
||||
ChunkType chunkType=headerBlock.getChunkType();
|
||||
if(chunkType==null){
|
||||
unknownChunk(reader, headerBlock);
|
||||
return;
|
||||
}
|
||||
if(chunkType==ChunkType.XML_START_ELEMENT){
|
||||
onStartElement(reader);
|
||||
}else if(chunkType==ChunkType.XML_END_ELEMENT){
|
||||
onEndElement(reader);
|
||||
}else if(chunkType==ChunkType.XML_START_NAMESPACE){
|
||||
onStartNamespace(reader);
|
||||
}else if(chunkType==ChunkType.XML_END_NAMESPACE){
|
||||
onEndNamespace(reader);
|
||||
}else if(chunkType==ChunkType.XML_CDATA){
|
||||
onXmlText(reader);
|
||||
}else{
|
||||
unexpectedChunk(reader, headerBlock);
|
||||
}
|
||||
if(!isBalanced()){
|
||||
if(!reader.isAvailable()){
|
||||
unBalancedFinish(reader);
|
||||
}else {
|
||||
readBytes(reader);
|
||||
}
|
||||
return;
|
||||
}
|
||||
linkStartEnd();
|
||||
onFinishedRead(reader, headerBlock);
|
||||
}
|
||||
private void onFinishedRead(BlockReader reader, HeaderBlock headerBlock) throws IOException{
|
||||
int avail=reader.available();
|
||||
if(avail>0 && getDepth()==0){
|
||||
onFinishedUnexpected(reader);
|
||||
return;
|
||||
}
|
||||
onFinishedSuccess(reader, headerBlock);
|
||||
}
|
||||
private void onFinishedSuccess(BlockReader reader, HeaderBlock headerBlock) throws IOException{
|
||||
|
||||
}
|
||||
private void onFinishedUnexpected(BlockReader reader) throws IOException{
|
||||
throw new IOException("Unexpected finish reading: "+reader.toString());
|
||||
}
|
||||
private void onStartElement(BlockReader reader) throws IOException{
|
||||
if(hasStartElement()){
|
||||
ResXmlElement childElement=new ResXmlElement();
|
||||
addElement(childElement);
|
||||
childElement.setDepth(getDepth()+1);
|
||||
childElement.readBytes(reader);
|
||||
}else{
|
||||
ResXmlStartElement startElement=new ResXmlStartElement();
|
||||
setStartElement(startElement);
|
||||
startElement.readBytes(reader);
|
||||
}
|
||||
}
|
||||
private void onEndElement(BlockReader reader) throws IOException{
|
||||
if(hasEndElement()){
|
||||
multipleEndElement(reader);
|
||||
return;
|
||||
}
|
||||
ResXmlEndElement endElement=new ResXmlEndElement();
|
||||
setEndElement(endElement);
|
||||
endElement.readBytes(reader);
|
||||
}
|
||||
private void onStartNamespace(BlockReader reader) throws IOException{
|
||||
ResXmlStartNamespace startNamespace=new ResXmlStartNamespace();
|
||||
addStartNamespace(startNamespace);
|
||||
startNamespace.readBytes(reader);
|
||||
}
|
||||
private void onEndNamespace(BlockReader reader) throws IOException{
|
||||
ResXmlEndNamespace endNamespace=new ResXmlEndNamespace();
|
||||
addEndNamespace(endNamespace);
|
||||
endNamespace.readBytes(reader);
|
||||
}
|
||||
private void onXmlText(BlockReader reader) throws IOException{
|
||||
ResXmlText xmlText=new ResXmlText();
|
||||
setResXmlText(xmlText);
|
||||
xmlText.readBytes(reader);
|
||||
}
|
||||
|
||||
private void unknownChunk(BlockReader reader, HeaderBlock headerBlock) throws IOException{
|
||||
throw new IOException("Unknown chunk: "+headerBlock.toString());
|
||||
}
|
||||
private void multipleEndElement(BlockReader reader) throws IOException{
|
||||
throw new IOException("Multiple end element: "+reader.toString());
|
||||
}
|
||||
private void unexpectedChunk(BlockReader reader, HeaderBlock headerBlock) throws IOException{
|
||||
throw new IOException("Unexpected chunk: "+headerBlock.toString());
|
||||
}
|
||||
private void unBalancedFinish(BlockReader reader) throws IOException{
|
||||
if(!isNamespaceBalanced()){
|
||||
throw new IOException("Unbalanced namespace: start="
|
||||
+mStartNamespaceList.size()+", end="+mEndNamespaceList.size());
|
||||
}
|
||||
if(!isElementBalanced()){
|
||||
throw new IOException("Unbalanced element: hasStart="
|
||||
+hasStartElement()+", hasEnd="+hasEndElement());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.reandroid.lib.arsc.chunk.xml;
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
|
||||
public class ResXmlEndElement extends BaseXmlChunk {
|
||||
private ResXmlStartElement mResXmlStartElement;
|
||||
public ResXmlEndElement(){
|
||||
super(ChunkType.XML_END_ELEMENT, 0);
|
||||
}
|
||||
|
||||
public void setResXmlStartElement(ResXmlStartElement element){
|
||||
mResXmlStartElement=element;
|
||||
}
|
||||
public ResXmlStartElement getResXmlStartElement(){
|
||||
return mResXmlStartElement;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.reandroid.lib.arsc.chunk.xml;
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
|
||||
public class ResXmlEndNamespace extends BaseXmlChunk {
|
||||
private ResXmlStartNamespace mResXmlStartNamespace;
|
||||
public ResXmlEndNamespace() {
|
||||
super(ChunkType.XML_END_NAMESPACE, 0);
|
||||
}
|
||||
|
||||
public void setResXmlStartNamespace(ResXmlStartNamespace ns){
|
||||
mResXmlStartNamespace=ns;
|
||||
}
|
||||
public ResXmlStartNamespace getResXmlStartNamespace(){
|
||||
return mResXmlStartNamespace;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getUri(){
|
||||
return getString(getUriReference());
|
||||
}
|
||||
public String getPrefix(){
|
||||
return getString(getPrefixReference());
|
||||
}
|
||||
public int getUriReference(){
|
||||
return getStringReference();
|
||||
}
|
||||
public void setUriReference(int ref){
|
||||
setStringReference(ref);
|
||||
if(mResXmlStartNamespace!=null){
|
||||
mResXmlStartNamespace.setStringReference(ref);
|
||||
}
|
||||
}
|
||||
public int getPrefixReference(){
|
||||
return getNamespaceReference();
|
||||
}
|
||||
public void setPrefixReference(int ref){
|
||||
setNamespaceReference(ref);
|
||||
if(mResXmlStartNamespace!=null){
|
||||
mResXmlStartNamespace.setNamespaceReference(ref);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
String uri=getUri();
|
||||
if(uri==null){
|
||||
return super.toString();
|
||||
}
|
||||
return "xmlns:"+getPrefix()+"=\""+getUri()+"\"";
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.reandroid.lib.arsc.chunk.xml;
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
import com.reandroid.lib.arsc.array.ResXmlIDArray;
|
||||
import com.reandroid.lib.arsc.chunk.BaseChunk;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ResXmlIDMap extends BaseChunk {
|
||||
private final ResXmlIDArray mResXmlIDArray;
|
||||
public ResXmlIDMap() {
|
||||
super(ChunkType.XML_RESOURCE_MAP, 1);
|
||||
this.mResXmlIDArray=new ResXmlIDArray(getHeaderBlock());
|
||||
addChild(mResXmlIDArray);
|
||||
}
|
||||
public ResXmlIDArray getResXmlIDArray(){
|
||||
return mResXmlIDArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChunkRefreshed() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
super.onReadBytes(reader);
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
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.ShortItem;
|
||||
|
||||
|
||||
public class ResXmlStartElement extends BaseXmlChunk {
|
||||
private final ShortItem mAttributeStart;
|
||||
private final ShortItem mAttributeUnitSize;
|
||||
private final ShortItem mAttributeCount;
|
||||
private final ShortItem mIdAttribute;
|
||||
private final IntegerItem mClassAttribute;
|
||||
private final ResXmlAttributeArray mAttributeArray;
|
||||
private ResXmlEndElement mResXmlEndElement;
|
||||
public ResXmlStartElement() {
|
||||
super(ChunkType.XML_START_ELEMENT, 6);
|
||||
mAttributeStart=new ShortItem(ATTRIBUTES_DEFAULT_START);
|
||||
mAttributeUnitSize =new ShortItem(ATTRIBUTES_UNIT_SIZE);
|
||||
mAttributeCount=new ShortItem();
|
||||
mIdAttribute=new ShortItem();
|
||||
mClassAttribute=new IntegerItem();
|
||||
mAttributeArray=new ResXmlAttributeArray(getHeaderBlock(), mAttributeStart, mAttributeCount);
|
||||
addChild(mAttributeStart);
|
||||
addChild(mAttributeUnitSize);
|
||||
addChild(mAttributeCount);
|
||||
addChild(mIdAttribute);
|
||||
addChild(mClassAttribute);
|
||||
addChild(mAttributeArray);
|
||||
}
|
||||
public String getTagName(){
|
||||
String prefix=getPrefix();
|
||||
String name=getName();
|
||||
if(prefix==null){
|
||||
return name;
|
||||
}
|
||||
return prefix+":"+name;
|
||||
}
|
||||
public String getPrefix(){
|
||||
int uriRef=getNamespaceReference();
|
||||
if(uriRef<0){
|
||||
return null;
|
||||
}
|
||||
ResXmlElement parentElement=getParentResXmlElement();
|
||||
ResXmlStartNamespace startNamespace=parentElement.getStartNamespaceByUriRef(uriRef);
|
||||
if(startNamespace!=null){
|
||||
return startNamespace.getPrefix();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public void setResXmlEndElement(ResXmlEndElement element){
|
||||
mResXmlEndElement=element;
|
||||
}
|
||||
public ResXmlEndElement getResXmlEndElement(){
|
||||
return mResXmlEndElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChunkRefreshed() {
|
||||
refreshAttributeStart();
|
||||
refreshAttributeCount();
|
||||
}
|
||||
private void refreshAttributeStart(){
|
||||
int start=countUpTo(mAttributeArray);
|
||||
start=start-getHeaderBlock().getHeaderSize();
|
||||
mAttributeStart.set((short)start);
|
||||
}
|
||||
private void refreshAttributeCount(){
|
||||
int count=mAttributeArray.childesCount();
|
||||
mAttributeCount.set((short)count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
String txt=getTagName();
|
||||
if(txt==null){
|
||||
return super.toString();
|
||||
}
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append("TAG: line=").append(getLineNumber()).append(" <").append(txt).append(">");
|
||||
ResXmlAttribute[] allAttr=mAttributeArray.getChildes();
|
||||
if(allAttr!=null){
|
||||
for(int i=0;i<allAttr.length;i++){
|
||||
if(i>10){
|
||||
break;
|
||||
}
|
||||
builder.append(", ");
|
||||
builder.append(allAttr[i].toString());
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static final short ATTRIBUTES_UNIT_SIZE=0x0014;
|
||||
private static final short ATTRIBUTES_DEFAULT_START=0x0014;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package com.reandroid.lib.arsc.chunk.xml;
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
|
||||
public class ResXmlStartNamespace extends BaseXmlChunk {
|
||||
private ResXmlEndNamespace mResXmlEndNamespace;
|
||||
public ResXmlStartNamespace() {
|
||||
super(ChunkType.XML_START_NAMESPACE, 0);
|
||||
}
|
||||
public void setResXmlEndNamespace(ResXmlEndNamespace ns){
|
||||
mResXmlEndNamespace=ns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUri(){
|
||||
return getString(getUriReference());
|
||||
}
|
||||
public String getPrefix(){
|
||||
return getString(getPrefixReference());
|
||||
}
|
||||
public int getUriReference(){
|
||||
return getStringReference();
|
||||
}
|
||||
public void setUriReference(int ref){
|
||||
setStringReference(ref);
|
||||
if(mResXmlEndNamespace!=null){
|
||||
mResXmlEndNamespace.setStringReference(ref);
|
||||
}
|
||||
}
|
||||
public int getPrefixReference(){
|
||||
return getNamespaceReference();
|
||||
}
|
||||
public void setPrefixReference(int ref){
|
||||
setNamespaceReference(ref);
|
||||
if(mResXmlEndNamespace!=null){
|
||||
mResXmlEndNamespace.setNamespaceReference(ref);
|
||||
}
|
||||
}
|
||||
|
||||
public ResXmlEndNamespace getResXmlEndNamespace(){
|
||||
return mResXmlEndNamespace;
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
String uri=getUri();
|
||||
if(uri==null){
|
||||
return super.toString();
|
||||
}
|
||||
return "xmlns:"+getPrefix()+"=\""+getUri()+"\"";
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
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;
|
||||
|
||||
public class ResXmlText extends BaseXmlChunk {
|
||||
private final IntegerItem mReserved;
|
||||
public ResXmlText() {
|
||||
super(ChunkType.XML_CDATA, 1);
|
||||
this.mReserved=new IntegerItem();
|
||||
addChild(mReserved);
|
||||
}
|
||||
public String getText(){
|
||||
ResXmlString xmlString=getResXmlString(getTextReference());
|
||||
if(xmlString!=null){
|
||||
return xmlString.getHtml();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public int getTextReference(){
|
||||
return getNamespaceReference();
|
||||
}
|
||||
public void setTextReference(int ref){
|
||||
setNamespaceReference(ref);
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
String txt=getText();
|
||||
if(txt!=null){
|
||||
return "TEXT: line="+getLineNumber()+" >"+txt+"<";
|
||||
}
|
||||
return super.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package com.reandroid.lib.arsc.container;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockCounter;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BlockList<T extends Block> extends Block {
|
||||
private final List<T> mItems;
|
||||
public BlockList(){
|
||||
super();
|
||||
mItems=new ArrayList<>();
|
||||
}
|
||||
public void add(T item){
|
||||
if(item==null){
|
||||
return;
|
||||
}
|
||||
item.setIndex(mItems.size());
|
||||
item.setParent(this);
|
||||
mItems.add(item);
|
||||
}
|
||||
public T get(int i){
|
||||
return mItems.get(i);
|
||||
}
|
||||
public int size(){
|
||||
return mItems.size();
|
||||
}
|
||||
public List<T> getChildes(){
|
||||
return mItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() {
|
||||
byte[] results=null;
|
||||
for(T item:mItems){
|
||||
if(item!=null){
|
||||
results=addBytes(results, item.getBytes());
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
@Override
|
||||
public int countBytes() {
|
||||
int result=0;
|
||||
for(T item:mItems){
|
||||
result+=item.countBytes();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCountUpTo(BlockCounter counter) {
|
||||
if(counter.FOUND){
|
||||
return;
|
||||
}
|
||||
if(counter.END==this){
|
||||
counter.FOUND=true;
|
||||
return;
|
||||
}
|
||||
for(T item:mItems){
|
||||
if(counter.FOUND){
|
||||
break;
|
||||
}
|
||||
item.onCountUpTo(counter);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected int onWriteBytes(OutputStream stream) throws IOException {
|
||||
int result=0;
|
||||
for(T item:mItems){
|
||||
result+=item.writeBytes(stream);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException{
|
||||
for(T item:mItems){
|
||||
item.readBytes(reader);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.reandroid.lib.arsc.container;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockContainer;
|
||||
|
||||
public class ExpandableBlockContainer extends BlockContainer<Block> {
|
||||
private Block[] mChildes;
|
||||
private int mCursor;
|
||||
public ExpandableBlockContainer(int initialSize){
|
||||
super();
|
||||
this.mChildes=new Block[initialSize];
|
||||
}
|
||||
public final void addChild(Block block){
|
||||
if(block==null){
|
||||
return;
|
||||
}
|
||||
int index=mCursor;
|
||||
ensureCount(index+1);
|
||||
mChildes[index]=block;
|
||||
block.setIndex(index);
|
||||
block.setParent(this);
|
||||
mCursor++;
|
||||
}
|
||||
private void ensureCount(int count){
|
||||
if(count<= childesCount()){
|
||||
return;
|
||||
}
|
||||
Block[] old=mChildes;
|
||||
mChildes=new Block[count];
|
||||
for(int i=0;i<old.length;i++){
|
||||
mChildes[i]=old[i];
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int childesCount() {
|
||||
return mChildes.length;
|
||||
}
|
||||
@Override
|
||||
public final Block[] getChildes() {
|
||||
return mChildes;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.reandroid.lib.arsc.container;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockContainer;
|
||||
|
||||
public class FixedBlockContainer extends BlockContainer<Block> {
|
||||
private final Block[] mChildes;
|
||||
public FixedBlockContainer(int childesCount){
|
||||
super();
|
||||
mChildes=new Block[childesCount];
|
||||
}
|
||||
public void addChild(int index, Block block){
|
||||
mChildes[index]=block;
|
||||
block.setIndex(index);
|
||||
block.setParent(this);
|
||||
}
|
||||
@Override
|
||||
protected void onRefreshed(){
|
||||
}
|
||||
@Override
|
||||
public int childesCount() {
|
||||
return mChildes.length;
|
||||
}
|
||||
@Override
|
||||
public Block[] getChildes() {
|
||||
return mChildes;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package com.reandroid.lib.arsc.container;
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
import com.reandroid.lib.arsc.array.SpecTypePairArray;
|
||||
import com.reandroid.lib.arsc.chunk.LibraryBlock;
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class PackageLastBlocks extends FixedBlockContainer {
|
||||
|
||||
private final SpecTypePairArray mSpecTypePairArray;
|
||||
private final LibraryBlock mLibraryBlock;
|
||||
public PackageLastBlocks(SpecTypePairArray specTypePairArray, LibraryBlock libraryBlock){
|
||||
super(2);
|
||||
this.mSpecTypePairArray=specTypePairArray;
|
||||
this.mLibraryBlock=libraryBlock;
|
||||
addChild(0, mSpecTypePairArray);
|
||||
addChild(1, mLibraryBlock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException{
|
||||
boolean readOk=true;
|
||||
while (readOk){
|
||||
readOk=readNextBlock(reader);
|
||||
}
|
||||
}
|
||||
private boolean readNextBlock(BlockReader reader) throws IOException {
|
||||
HeaderBlock headerBlock = reader.readHeaderBlock();
|
||||
if(headerBlock==null){
|
||||
return false;
|
||||
}
|
||||
int pos=reader.getPosition();
|
||||
ChunkType chunkType=headerBlock.getChunkType();
|
||||
if(chunkType==ChunkType.SPEC){
|
||||
readSpecBlock(reader);
|
||||
}else if(chunkType==ChunkType.LIBRARY){
|
||||
readLibraryBlock(reader);
|
||||
}else {
|
||||
readUnexpectedBlock(reader, headerBlock);
|
||||
}
|
||||
return pos!=reader.getPosition();
|
||||
}
|
||||
private void readSpecBlock(BlockReader reader) throws IOException{
|
||||
SpecTypePair specTypePair=mSpecTypePairArray.createNext();
|
||||
specTypePair.readBytes(reader);
|
||||
}
|
||||
private void readLibraryBlock(BlockReader reader) throws IOException{
|
||||
LibraryBlock libraryBlock=new LibraryBlock();
|
||||
libraryBlock.readBytes(reader);
|
||||
mLibraryBlock.addLibraryInfo(libraryBlock);
|
||||
}
|
||||
private void readUnexpectedBlock(BlockReader reader, HeaderBlock headerBlock) throws IOException{
|
||||
throw new IOException(reader.getActualPosition()+", Unexpected block: "+headerBlock.toString());
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.reandroid.lib.arsc.container;
|
||||
|
||||
import com.reandroid.lib.arsc.base.BlockContainer;
|
||||
import com.reandroid.lib.arsc.value.BaseResValue;
|
||||
|
||||
public class ResValueContainer extends BlockContainer<BaseResValue> {
|
||||
private final BaseResValue[] mChildes;
|
||||
public ResValueContainer(){
|
||||
super();
|
||||
mChildes=new BaseResValue[1];
|
||||
}
|
||||
@Override
|
||||
protected void onRefreshed(){
|
||||
}
|
||||
@Override
|
||||
public int childesCount() {
|
||||
return mChildes.length;
|
||||
}
|
||||
@Override
|
||||
public BaseResValue[] getChildes() {
|
||||
return mChildes;
|
||||
}
|
||||
public void setResValue(BaseResValue resValue){
|
||||
BaseResValue old=getResValue();
|
||||
if(old!=null){
|
||||
old.setIndex(-1);
|
||||
old.setParent(null);
|
||||
}
|
||||
mChildes[0]=resValue;
|
||||
if(resValue==null){
|
||||
return;
|
||||
}
|
||||
resValue.setIndex(0);
|
||||
resValue.setParent(this);
|
||||
}
|
||||
public BaseResValue getResValue(){
|
||||
if(mChildes.length==0){
|
||||
return null;
|
||||
}
|
||||
return mChildes[0];
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
package com.reandroid.lib.arsc.container;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockContainer;
|
||||
import com.reandroid.lib.arsc.base.BlockCounter;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class SingleBlockContainer<T extends Block> extends BlockContainer<T> {
|
||||
private T mItem;
|
||||
public SingleBlockContainer(){
|
||||
super();
|
||||
}
|
||||
@Override
|
||||
protected void refreshChildes(){
|
||||
if(mItem!=null){
|
||||
if(mItem instanceof BlockContainer){
|
||||
((BlockContainer)mItem).refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
|
||||
}
|
||||
public T getItem() {
|
||||
return mItem;
|
||||
}
|
||||
public void setItem(T item) {
|
||||
if(item==null){
|
||||
if(mItem!=null){
|
||||
mItem.setIndex(-1);
|
||||
mItem.setParent(null);
|
||||
}
|
||||
mItem=null;
|
||||
return;
|
||||
}
|
||||
this.mItem = item;
|
||||
item.setIndex(getIndex());
|
||||
item.setParent(this);
|
||||
}
|
||||
public boolean hasItem(){
|
||||
return this.mItem!=null;
|
||||
}
|
||||
@Override
|
||||
public byte[] getBytes() {
|
||||
if(mItem!=null){
|
||||
return mItem.getBytes();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public int countBytes() {
|
||||
if(mItem!=null){
|
||||
return mItem.countBytes();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCountUpTo(BlockCounter counter) {
|
||||
if(counter.FOUND){
|
||||
return;
|
||||
}
|
||||
if(counter.END==this){
|
||||
counter.FOUND=true;
|
||||
return;
|
||||
}
|
||||
if(mItem!=null){
|
||||
mItem.onCountUpTo(counter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException{
|
||||
if(mItem!=null){
|
||||
mItem.readBytes(reader);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public int onWriteBytes(OutputStream stream) throws IOException {
|
||||
if(mItem!=null){
|
||||
return mItem.writeBytes(stream);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int childesCount() {
|
||||
return hasItem()?0:1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T[] getChildes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
if(mItem!=null){
|
||||
return mItem.toString();
|
||||
}
|
||||
return getClass().getSimpleName()+": EMPTY";
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package com.reandroid.lib.arsc.container;
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
import com.reandroid.lib.arsc.array.TypeBlockArray;
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockContainer;
|
||||
import com.reandroid.lib.arsc.chunk.PackageBlock;
|
||||
import com.reandroid.lib.arsc.chunk.SpecBlock;
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.value.ResConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class SpecTypePair extends BlockContainer<Block> {
|
||||
private final Block[] mChildes;
|
||||
private final SpecBlock mSpecBlock;
|
||||
private final TypeBlockArray mTypeBlockArray;
|
||||
public SpecTypePair(SpecBlock specBlock, TypeBlockArray typeBlockArray){
|
||||
this.mSpecBlock = specBlock;
|
||||
this.mTypeBlockArray = typeBlockArray;
|
||||
this.mChildes=new Block[]{specBlock, typeBlockArray};
|
||||
mSpecBlock.setIndex(0);
|
||||
mTypeBlockArray.setIndex(1);
|
||||
mSpecBlock.setParent(this);
|
||||
mTypeBlockArray.setParent(this);
|
||||
}
|
||||
public SpecTypePair(){
|
||||
this(new SpecBlock(), new TypeBlockArray());
|
||||
}
|
||||
public List<ResConfig> listResConfig(){
|
||||
return mTypeBlockArray.listResConfig();
|
||||
}
|
||||
public byte getTypeId(){
|
||||
return mSpecBlock.getTypeId();
|
||||
}
|
||||
public void setTypeId(byte id){
|
||||
mSpecBlock.setTypeId(id);
|
||||
mTypeBlockArray.setTypeId(id);
|
||||
}
|
||||
public SpecBlock getSpecBlock(){
|
||||
return mSpecBlock;
|
||||
}
|
||||
public TypeBlockArray getTypeBlockArray(){
|
||||
return mTypeBlockArray;
|
||||
}
|
||||
public PackageBlock getPackageBlock(){
|
||||
Block parent=getParent();
|
||||
while (parent!=null){
|
||||
if(parent instanceof PackageBlock){
|
||||
return (PackageBlock)parent;
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public int childesCount() {
|
||||
return mChildes.length;
|
||||
}
|
||||
@Override
|
||||
public Block[] getChildes() {
|
||||
return mChildes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
HeaderBlock headerBlock=reader.readHeaderBlock();
|
||||
if(headerBlock==null){
|
||||
return;
|
||||
}
|
||||
ChunkType chunkType=headerBlock.getChunkType();
|
||||
if(chunkType!=ChunkType.SPEC){
|
||||
readUnexpectedNonSpecBlock(reader, headerBlock);
|
||||
}
|
||||
mSpecBlock.readBytes(reader);
|
||||
mTypeBlockArray.readBytes(reader);
|
||||
}
|
||||
private void readUnexpectedNonSpecBlock(BlockReader reader, HeaderBlock headerBlock) throws IOException{
|
||||
throw new IOException("Unexpected block: "+headerBlock.toString()+", Should be: "+ChunkType.SPEC);
|
||||
}
|
||||
|
||||
}
|
98
src/main/java/com/reandroid/lib/arsc/header/HeaderBlock.java
Normal file
98
src/main/java/com/reandroid/lib/arsc/header/HeaderBlock.java
Normal file
@ -0,0 +1,98 @@
|
||||
package com.reandroid.lib.arsc.header;
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.container.ExpandableBlockContainer;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.ShortItem;
|
||||
|
||||
public class HeaderBlock extends ExpandableBlockContainer {
|
||||
private final ShortItem mType;
|
||||
private final ShortItem mHeaderSize;
|
||||
private final IntegerItem mChunkSize;
|
||||
public HeaderBlock(short type){
|
||||
super(3);
|
||||
this.mType=new ShortItem(type);
|
||||
this.mHeaderSize=new ShortItem();
|
||||
this.mChunkSize=new IntegerItem();
|
||||
addChild(mType);
|
||||
addChild(mHeaderSize);
|
||||
addChild(mChunkSize);
|
||||
}
|
||||
public ChunkType getChunkType(){
|
||||
return ChunkType.get(mType.get());
|
||||
}
|
||||
public short getType(){
|
||||
return mType.get();
|
||||
}
|
||||
public void setType(ChunkType chunkType){
|
||||
short type;
|
||||
if(chunkType==null){
|
||||
type=0;
|
||||
}else {
|
||||
type=chunkType.ID;
|
||||
}
|
||||
setType(type);
|
||||
}
|
||||
public void setType(short type){
|
||||
mType.set(type);
|
||||
}
|
||||
|
||||
public short getHeaderSize(){
|
||||
return mHeaderSize.get();
|
||||
}
|
||||
public void setHeaderSize(short headerSize){
|
||||
mHeaderSize.set(headerSize);
|
||||
}
|
||||
public int getChunkSize(){
|
||||
return mChunkSize.get();
|
||||
}
|
||||
public void setChunkSize(int chunkSize){
|
||||
mChunkSize.set(chunkSize);
|
||||
}
|
||||
|
||||
public final void refreshHeader(){
|
||||
refreshHeaderSize();
|
||||
refreshChunkSize();
|
||||
}
|
||||
private void refreshHeaderSize(){
|
||||
int count=countBytes();
|
||||
setHeaderSize((short)count);
|
||||
}
|
||||
private void refreshChunkSize(){
|
||||
Block parent=getParent();
|
||||
if(parent==null){
|
||||
return;
|
||||
}
|
||||
int count=parent.countBytes();
|
||||
setChunkSize(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
// Not required, the parent should call refreshHeader()
|
||||
}
|
||||
@Override
|
||||
protected void refreshChildes(){
|
||||
// Not required
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
short t= getType();
|
||||
ChunkType type= ChunkType.get(t);
|
||||
StringBuilder builder=new StringBuilder();
|
||||
if(type!=null){
|
||||
builder.append(type.toString());
|
||||
}else {
|
||||
builder.append("Unknown type=");
|
||||
builder.append(String.format("0x%02x", ((int)t)));
|
||||
}
|
||||
builder.append("{Header=");
|
||||
builder.append(getHeaderSize());
|
||||
builder.append(", Chunk=");
|
||||
builder.append(getChunkSize());
|
||||
builder.append("}");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
9
src/main/java/com/reandroid/lib/arsc/io/BlockLoad.java
Normal file
9
src/main/java/com/reandroid/lib/arsc/io/BlockLoad.java
Normal file
@ -0,0 +1,9 @@
|
||||
package com.reandroid.lib.arsc.io;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface BlockLoad {
|
||||
public void onBlockLoaded(BlockReader reader, Block sender) throws IOException;
|
||||
}
|
327
src/main/java/com/reandroid/lib/arsc/io/BlockReader.java
Normal file
327
src/main/java/com/reandroid/lib/arsc/io/BlockReader.java
Normal file
@ -0,0 +1,327 @@
|
||||
package com.reandroid.lib.arsc.io;
|
||||
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
||||
public class BlockReader extends InputStream {
|
||||
private final Object mLock=new Object();
|
||||
private byte[] BUFFER;
|
||||
private final int mStart;
|
||||
private final int mLength;
|
||||
private int mPosition;
|
||||
private boolean mIsClosed;
|
||||
private int mMark;
|
||||
public BlockReader(byte[] buffer, int start, int length) {
|
||||
this.BUFFER=buffer;
|
||||
this.mStart=start;
|
||||
this.mLength=length;
|
||||
this.mPosition =0;
|
||||
}
|
||||
public BlockReader(byte[] buffer) {
|
||||
this(buffer, 0, buffer.length);
|
||||
}
|
||||
public BlockReader(InputStream in) throws IOException {
|
||||
this(loadBuffer(in));
|
||||
}
|
||||
public BlockReader(File file) throws IOException {
|
||||
this(loadBuffer(file));
|
||||
}
|
||||
public HeaderBlock readHeaderBlock() throws IOException {
|
||||
int pos=getPosition();
|
||||
HeaderBlock headerBlock=new HeaderBlock((short)0);
|
||||
try{
|
||||
headerBlock.readBytes(this);
|
||||
}catch (EOFException ex){
|
||||
return null;
|
||||
}
|
||||
seek(pos);
|
||||
return headerBlock;
|
||||
}
|
||||
public int searchNextIntPosition(int bytesOffset, int value){
|
||||
if(mIsClosed || mPosition>=mLength){
|
||||
return -1;
|
||||
}
|
||||
synchronized (mLock){
|
||||
int actPos=mStart+mPosition+bytesOffset;
|
||||
int max=available()/4;
|
||||
for(int i=0;i<max;i++){
|
||||
int pos=actPos+(i*4);
|
||||
int valCur=toInt(BUFFER, pos);
|
||||
if(valCur==value){
|
||||
return pos-mStart;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
private int toInt(byte[] bts, int offset){
|
||||
return bts[offset] & 0xff |
|
||||
(bts[offset+1] & 0xff) << 8 |
|
||||
(bts[offset+2] & 0xff) << 16 |
|
||||
(bts[offset+3] & 0xff) << 24;
|
||||
}
|
||||
public byte[] getBuffer(){
|
||||
return BUFFER;
|
||||
}
|
||||
public BlockReader create(int start, int len){
|
||||
int max=start+len;
|
||||
if(len<0 || max> mLength){
|
||||
len= mLength -start;
|
||||
}
|
||||
start=start+mStart;
|
||||
return new BlockReader(BUFFER, start, len);
|
||||
}
|
||||
public boolean isAvailable(){
|
||||
if(mIsClosed){
|
||||
return false;
|
||||
}
|
||||
return available()>0;
|
||||
}
|
||||
public void offset(int off){
|
||||
int pos=getPosition()+off;
|
||||
seek(pos);
|
||||
}
|
||||
public void seek(int relPos){
|
||||
if(relPos<0){
|
||||
relPos=0;
|
||||
}else if(relPos>length()){
|
||||
relPos=length();
|
||||
}
|
||||
setPosition(relPos);
|
||||
}
|
||||
private void setPosition(int pos){
|
||||
if(pos==mPosition){
|
||||
return;
|
||||
}
|
||||
synchronized (mLock){
|
||||
mPosition=pos;
|
||||
}
|
||||
}
|
||||
public int length(){
|
||||
return mLength;
|
||||
}
|
||||
public byte[] readBytes(int len) throws IOException {
|
||||
byte[] result=new byte[len];
|
||||
if(len==0){
|
||||
return result;
|
||||
}
|
||||
int len2=read(result);
|
||||
if(len2<0){
|
||||
throw new EOFException("Finished reading: "+ mPosition);
|
||||
}
|
||||
if(len==len2){
|
||||
return result;
|
||||
}
|
||||
byte[] result2=new byte[len2];
|
||||
System.arraycopy(result, 0, result2, 0, len2);
|
||||
return result2;
|
||||
}
|
||||
public int lengthUntilNextZero(int offset, int zeroCount){
|
||||
if(mIsClosed || mPosition>=mLength){
|
||||
return 0;
|
||||
}
|
||||
synchronized (mLock){
|
||||
int actPos=mStart+mPosition-1;
|
||||
int max=available();
|
||||
int remZero=zeroCount;
|
||||
int len=0;
|
||||
for(int i=0;i<max;i++){
|
||||
actPos++;
|
||||
len++;
|
||||
if(i<offset){
|
||||
continue;
|
||||
}
|
||||
if(BUFFER[actPos]!=0){
|
||||
remZero=zeroCount;
|
||||
continue;
|
||||
}
|
||||
remZero--;
|
||||
if(remZero<=0){
|
||||
break;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
public int lengthUntilLastZero(int offset){
|
||||
if(mIsClosed || mPosition>=mLength){
|
||||
return 0;
|
||||
}
|
||||
synchronized (mLock){
|
||||
int actPos=mStart+mPosition-1;
|
||||
int max=available();
|
||||
int len=0;
|
||||
boolean zeroFound=false;
|
||||
for(int i=0;i<max;i++){
|
||||
actPos++;
|
||||
len++;
|
||||
if(i<offset){
|
||||
continue;
|
||||
}
|
||||
byte b=BUFFER[actPos];
|
||||
if(b==0){
|
||||
zeroFound=true;
|
||||
}else if(zeroFound){
|
||||
len--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
public int readFully(byte[] bts) throws IOException{
|
||||
return readFully(bts, 0, bts.length);
|
||||
}
|
||||
public int readFully(byte[] bts, int length) throws IOException{
|
||||
if(length==0){
|
||||
return 0;
|
||||
}
|
||||
return readFully(bts, 0, length);
|
||||
}
|
||||
|
||||
public int skipByteValues(byte b) throws IOException {
|
||||
if(mIsClosed){
|
||||
throw new IOException("Stream is closed");
|
||||
}
|
||||
if(mPosition>=mLength){
|
||||
throw new EOFException("Finished reading: "+mPosition);
|
||||
}
|
||||
synchronized (mLock){
|
||||
int i=0;
|
||||
while (mPosition<mLength){
|
||||
if(BUFFER[mStart+mPosition]!=b){
|
||||
return i;
|
||||
}
|
||||
mPosition++;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
public int readFully(byte[] bts, int start, int length) throws IOException {
|
||||
if(length==0){
|
||||
return 0;
|
||||
}
|
||||
if(mIsClosed){
|
||||
throw new IOException("Stream is closed");
|
||||
}
|
||||
if(mPosition>=mLength){
|
||||
throw new EOFException("Finished reading: "+mPosition);
|
||||
}
|
||||
int len=bts.length;
|
||||
if(length<len){
|
||||
len=length;
|
||||
}
|
||||
synchronized (mLock){
|
||||
int actPos=mStart+mPosition;
|
||||
int j;
|
||||
for(j=0;j<len;j++){
|
||||
bts[start+j]=BUFFER[actPos+j];
|
||||
mPosition++;
|
||||
if(mPosition>=mLength){
|
||||
j++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
}
|
||||
public int getPosition(){
|
||||
return mPosition;
|
||||
}
|
||||
public int getActualPosition(){
|
||||
return mStart + mPosition;
|
||||
}
|
||||
public int getStartPosition(){
|
||||
return mStart;
|
||||
}
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if(mIsClosed){
|
||||
throw new IOException("Stream is closed");
|
||||
}
|
||||
int i=mPosition;
|
||||
if(i>=mLength){
|
||||
throw new EOFException("Finished reading: "+i);
|
||||
}
|
||||
synchronized (mLock){
|
||||
int actPos=mStart+i;
|
||||
int val=BUFFER[actPos] & 0xff;
|
||||
mPosition++;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void mark(int pos){
|
||||
mMark=pos;
|
||||
}
|
||||
@Override
|
||||
public int available(){
|
||||
return mLength-mPosition;
|
||||
}
|
||||
@Override
|
||||
public void reset() throws IOException{
|
||||
if(mIsClosed){
|
||||
throw new IOException("Can not reset stream is closed");
|
||||
}
|
||||
mPosition=mMark;
|
||||
}
|
||||
@Override
|
||||
public void close(){
|
||||
mIsClosed=true;
|
||||
BUFFER=null;
|
||||
mMark=0;
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(getClass().getSimpleName());
|
||||
builder.append(": ");
|
||||
if(mIsClosed){
|
||||
builder.append("Closed");
|
||||
}else{
|
||||
int av=available();
|
||||
if(av==0){
|
||||
builder.append("Finished: ");
|
||||
builder.append(getPosition());
|
||||
}else {
|
||||
if(mStart>0){
|
||||
builder.append("START=");
|
||||
builder.append(mStart);
|
||||
builder.append(", ACTUAL=");
|
||||
builder.append(getActualPosition());
|
||||
builder.append(", ");
|
||||
}
|
||||
builder.append("POS=");
|
||||
builder.append(getPosition());
|
||||
builder.append(", available=");
|
||||
builder.append(av);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
||||
private static byte[] loadBuffer(File file) throws IOException {
|
||||
FileInputStream in=new FileInputStream(file);
|
||||
return loadBuffer(in);
|
||||
}
|
||||
private static byte[] loadBuffer(InputStream in) throws IOException {
|
||||
byte[] result=new byte[0];
|
||||
byte[] buff=new byte[40960];
|
||||
int len;
|
||||
while((len=in.read(buff))>0){
|
||||
result=add(result, buff, len);
|
||||
}
|
||||
in.close();
|
||||
return result;
|
||||
}
|
||||
private static byte[] add(byte[] arr1, byte[] arr2, int len){
|
||||
byte[] result=new byte[arr1.length + len];
|
||||
System.arraycopy(arr1, 0, result, 0, arr1.length);
|
||||
System.arraycopy(arr2, 0, result, arr1.length, len);
|
||||
return result;
|
||||
}
|
||||
}
|
101
src/main/java/com/reandroid/lib/arsc/item/BlockItem.java
Normal file
101
src/main/java/com/reandroid/lib/arsc/item/BlockItem.java
Normal file
@ -0,0 +1,101 @@
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockCounter;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public abstract class BlockItem extends Block {
|
||||
|
||||
private byte[] mBytes;
|
||||
public BlockItem(int bytesLength){
|
||||
super();
|
||||
mBytes=new byte[bytesLength];
|
||||
}
|
||||
public abstract void onBytesChanged();
|
||||
protected byte[] getBytesInternal() {
|
||||
return mBytes;
|
||||
}
|
||||
void setBytesInternal(byte[] bts){
|
||||
if(bts==null){
|
||||
bts=new byte[0];
|
||||
}
|
||||
if(bts==mBytes){
|
||||
return;
|
||||
}
|
||||
mBytes=bts;
|
||||
onBytesChanged();
|
||||
}
|
||||
final void ensureMinLength(int minLen){
|
||||
int len=mBytes.length;
|
||||
if(minLen<=len){
|
||||
return;
|
||||
}
|
||||
byte[] bts=new byte[minLen];
|
||||
System.arraycopy(bts, 0, mBytes, 0, mBytes.length);
|
||||
mBytes=bts;
|
||||
}
|
||||
final void setBytesLength(int length){
|
||||
setBytesLength(length, true);
|
||||
}
|
||||
final void setBytesLength(int length, boolean notify){
|
||||
if(length<0){
|
||||
length=0;
|
||||
}
|
||||
int old=mBytes.length;
|
||||
if(length==old){
|
||||
return;
|
||||
}
|
||||
byte[] bts=new byte[length];
|
||||
if(length<old){
|
||||
old=length;
|
||||
}
|
||||
System.arraycopy(mBytes, 0, bts, 0, old);
|
||||
mBytes=bts;
|
||||
if(notify){
|
||||
onBytesChanged();
|
||||
}
|
||||
}
|
||||
int getBytesLength(){
|
||||
return mBytes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countBytes() {
|
||||
if(isNull()){
|
||||
return 0;
|
||||
}
|
||||
return getBytesInternal().length;
|
||||
}
|
||||
@Override
|
||||
public byte[] getBytes() {
|
||||
if(isNull()){
|
||||
return null;
|
||||
}
|
||||
return getBytesInternal();
|
||||
}
|
||||
@Override
|
||||
public void onCountUpTo(BlockCounter counter) {
|
||||
if(counter.FOUND){
|
||||
return;
|
||||
}
|
||||
if(counter.END==this){
|
||||
counter.FOUND=true;
|
||||
return;
|
||||
}
|
||||
counter.addCount(countBytes());
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException{
|
||||
byte[] bts=getBytesInternal();
|
||||
reader.readFully(bts);
|
||||
onBytesChanged();
|
||||
}
|
||||
@Override
|
||||
protected int onWriteBytes(OutputStream stream) throws IOException {
|
||||
stream.write(getBytesInternal());
|
||||
return getBytesLength();
|
||||
}
|
||||
}
|
91
src/main/java/com/reandroid/lib/arsc/item/ByteArray.java
Normal file
91
src/main/java/com/reandroid/lib/arsc/item/ByteArray.java
Normal file
@ -0,0 +1,91 @@
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.List;
|
||||
|
||||
public class ByteArray extends BlockItem {
|
||||
public ByteArray(int bytesLength) {
|
||||
super(bytesLength);
|
||||
}
|
||||
public ByteArray() {
|
||||
this(0);
|
||||
}
|
||||
public final void clear(){
|
||||
setSize(0);
|
||||
}
|
||||
public final void add(byte[] values){
|
||||
if(values==null || values.length==0){
|
||||
return;
|
||||
}
|
||||
int old=size();
|
||||
int len=values.length;
|
||||
setBytesLength(old+len, false);
|
||||
byte[] bts = getBytesInternal();
|
||||
for(int i=0;i<len;i++){
|
||||
bts[old+i]=values[i];
|
||||
}
|
||||
}
|
||||
public final void set(byte[] values){
|
||||
super.setBytesInternal(values);
|
||||
}
|
||||
public final List<Byte> toList(){
|
||||
List<Byte> results=new AbstractList<Byte>() {
|
||||
@Override
|
||||
public Byte get(int i) {
|
||||
return ByteArray.this.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return ByteArray.this.size();
|
||||
}
|
||||
};
|
||||
return results;
|
||||
}
|
||||
public final byte[] toArray(){
|
||||
return getBytes();
|
||||
}
|
||||
public final void fill(byte value){
|
||||
byte[] bts=getBytesInternal();
|
||||
int max=bts.length;
|
||||
for(int i=0;i<max;i++){
|
||||
bts[i]=value;
|
||||
}
|
||||
}
|
||||
public final void ensureArraySize(int s){
|
||||
int sz=size();
|
||||
if(sz>=s){
|
||||
return;
|
||||
}
|
||||
setSize(s);
|
||||
}
|
||||
public final void setSize(int s){
|
||||
if(s<0){
|
||||
s=0;
|
||||
}
|
||||
setBytesLength(s);
|
||||
}
|
||||
public Byte get(int index){
|
||||
if(index<0 || index>=size()){
|
||||
return null;
|
||||
}
|
||||
return getBytesInternal()[index];
|
||||
}
|
||||
public final int size(){
|
||||
return getBytesLength();
|
||||
}
|
||||
final void add(byte value){
|
||||
int len=getBytesLength();
|
||||
len=len + 1;
|
||||
setBytesLength(len, false);
|
||||
put(len, value);
|
||||
}
|
||||
public final void put(int index, byte value){
|
||||
byte[] bts = getBytesInternal();
|
||||
bts[index]=value;
|
||||
}
|
||||
@Override
|
||||
public void onBytesChanged() {
|
||||
|
||||
}
|
||||
}
|
21
src/main/java/com/reandroid/lib/arsc/item/ByteItem.java
Normal file
21
src/main/java/com/reandroid/lib/arsc/item/ByteItem.java
Normal file
@ -0,0 +1,21 @@
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
|
||||
public class ByteItem extends BlockItem {
|
||||
public ByteItem() {
|
||||
super(1);
|
||||
}
|
||||
public void set(byte b){
|
||||
getBytesInternal()[0]=b;
|
||||
}
|
||||
public byte get(){
|
||||
return getBytesInternal()[0];
|
||||
}
|
||||
@Override
|
||||
public void onBytesChanged() {
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return String.valueOf(get());
|
||||
}
|
||||
}
|
112
src/main/java/com/reandroid/lib/arsc/item/IntegerArray.java
Normal file
112
src/main/java/com/reandroid/lib/arsc/item/IntegerArray.java
Normal file
@ -0,0 +1,112 @@
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.List;
|
||||
|
||||
public class IntegerArray extends BlockItem {
|
||||
public IntegerArray() {
|
||||
super(0);
|
||||
}
|
||||
public final void clear(){
|
||||
setSize(0);
|
||||
}
|
||||
public final void add(int[] values){
|
||||
if(values==null || values.length==0){
|
||||
return;
|
||||
}
|
||||
int old=size();
|
||||
int s=old+values.length;
|
||||
setSize(s);
|
||||
for(int i=0;i<s;i++){
|
||||
put(old+i, values[i]);
|
||||
}
|
||||
}
|
||||
public final void set(int[] values){
|
||||
if(values==null || values.length==0){
|
||||
setSize(0);
|
||||
return;
|
||||
}
|
||||
int s=values.length;
|
||||
setSize(s);
|
||||
for(int i=0;i<s;i++){
|
||||
put(i, values[i]);
|
||||
}
|
||||
}
|
||||
public final List<Integer> toList(){
|
||||
List<Integer> results=new AbstractList<Integer>() {
|
||||
@Override
|
||||
public Integer get(int i) {
|
||||
return IntegerArray.this.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return IntegerArray.this.size();
|
||||
}
|
||||
};
|
||||
return results;
|
||||
}
|
||||
public final int[] toArray(){
|
||||
int s=size();
|
||||
int[] result=new int[s];
|
||||
for(int i=0;i<s;i++){
|
||||
result[i]=get(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public final void fill(int value){
|
||||
int max=size();
|
||||
for(int i=0;i<max;i++){
|
||||
put(i, value);
|
||||
}
|
||||
}
|
||||
public final void ensureArraySize(int s){
|
||||
int sz=size();
|
||||
if(sz>=s){
|
||||
return;
|
||||
}
|
||||
setSize(s);
|
||||
}
|
||||
public final void setSize(int s){
|
||||
if(s<0){
|
||||
s=0;
|
||||
}
|
||||
int len=s*4;
|
||||
setBytesLength(len);
|
||||
}
|
||||
public Integer get(int index){
|
||||
if(index<0 || index>=size()){
|
||||
return null;
|
||||
}
|
||||
int i=index*4;
|
||||
byte[] bts = getBytesInternal();
|
||||
return bts[i] & 0xff |
|
||||
(bts[i+1] & 0xff) << 8 |
|
||||
(bts[i+2] & 0xff) << 16 |
|
||||
(bts[i+3] & 0xff) << 24;
|
||||
}
|
||||
public final int size(){
|
||||
return getBytesLength()/4;
|
||||
}
|
||||
final void add(int value){
|
||||
int len=getBytesLength();
|
||||
len=len + 4;
|
||||
setBytesLength(len, false);
|
||||
int pos=size()-1;
|
||||
put(pos, value);
|
||||
}
|
||||
public final void put(int index, int value){
|
||||
int i=index*4;
|
||||
byte[] bts = getBytesInternal();
|
||||
bts[i+3]= (byte) (value >>> 24 & 0xff);
|
||||
bts[i+2]= (byte) (value >>> 16 & 0xff);
|
||||
bts[i+1]= (byte) (value >>> 8 & 0xff);
|
||||
bts[i]= (byte) (value & 0xff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBytesChanged() {
|
||||
|
||||
}
|
||||
}
|
46
src/main/java/com/reandroid/lib/arsc/item/IntegerItem.java
Normal file
46
src/main/java/com/reandroid/lib/arsc/item/IntegerItem.java
Normal file
@ -0,0 +1,46 @@
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
|
||||
public class IntegerItem extends BlockItem {
|
||||
private int mCache;
|
||||
public IntegerItem(){
|
||||
super(4);
|
||||
}
|
||||
public IntegerItem(int val){
|
||||
this();
|
||||
set(val);
|
||||
}
|
||||
public void set(int val){
|
||||
if(val==mCache){
|
||||
return;
|
||||
}
|
||||
mCache=val;
|
||||
byte[] bts = getBytesInternal();
|
||||
bts[3]= (byte) (val >>> 24 & 0xff);
|
||||
bts[2]= (byte) (val >>> 16 & 0xff);
|
||||
bts[1]= (byte) (val >>> 8 & 0xff);
|
||||
bts[0]= (byte) (val & 0xff);
|
||||
}
|
||||
public int get(){
|
||||
return mCache;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onBytesChanged() {
|
||||
mCache=readIntBytes();
|
||||
}
|
||||
private int readIntBytes(){
|
||||
byte[] bts = getBytesInternal();
|
||||
return bts[0] & 0xff |
|
||||
(bts[1] & 0xff) << 8 |
|
||||
(bts[2] & 0xff) << 16 |
|
||||
(bts[3] & 0xff) << 24;
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return String.valueOf(get());
|
||||
}
|
||||
|
||||
}
|
70
src/main/java/com/reandroid/lib/arsc/item/PackageName.java
Normal file
70
src/main/java/com/reandroid/lib/arsc/item/PackageName.java
Normal file
@ -0,0 +1,70 @@
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class PackageName extends StringItem {
|
||||
public PackageName() {
|
||||
super(true);
|
||||
setBytesLength(BYTES_LENGTH);
|
||||
}
|
||||
@Override
|
||||
byte[] encodeString(String str){
|
||||
if(str==null){
|
||||
return new byte[BYTES_LENGTH];
|
||||
}
|
||||
byte[] bts=getUtf16Bytes(str);
|
||||
byte[] results=new byte[BYTES_LENGTH];
|
||||
int len=bts.length;
|
||||
if(len>BYTES_LENGTH){
|
||||
len=BYTES_LENGTH;
|
||||
}
|
||||
System.arraycopy(bts, 0, results, 0, len);
|
||||
return results;
|
||||
}
|
||||
@Override
|
||||
String decodeString(){
|
||||
return decodeUtf16Bytes(getBytesInternal());
|
||||
}
|
||||
@Override
|
||||
StyleItem getStyle(){
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
int calculateReadLength(BlockReader reader){
|
||||
return BYTES_LENGTH;
|
||||
}
|
||||
private static String decodeUtf16Bytes(byte[] bts){
|
||||
if(isNullBytes(bts)){
|
||||
return null;
|
||||
}
|
||||
int len=getEndNullPosition(bts);
|
||||
return new String(bts,0, len, StandardCharsets.UTF_16LE);
|
||||
}
|
||||
private static int getEndNullPosition(byte[] bts){
|
||||
int max=bts.length;
|
||||
int result=0;
|
||||
boolean found=false;
|
||||
for(int i=1; i<max;i++){
|
||||
byte b0=bts[i-1];
|
||||
byte b1=bts[i];
|
||||
if(b0==0 && b1==0){
|
||||
if(!found){
|
||||
result=i;
|
||||
found=true;
|
||||
}else if(result<i-1){
|
||||
return result;
|
||||
}
|
||||
}else {
|
||||
found=false;
|
||||
}
|
||||
}
|
||||
if(!found){
|
||||
return max;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private static final int BYTES_LENGTH=256;
|
||||
|
||||
}
|
14
src/main/java/com/reandroid/lib/arsc/item/ResXmlID.java
Normal file
14
src/main/java/com/reandroid/lib/arsc/item/ResXmlID.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
public class ResXmlID extends IntegerItem {
|
||||
public ResXmlID(){
|
||||
super();
|
||||
}
|
||||
public ResXmlID(int resId){
|
||||
super(resId);
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return getIndex()+": "+String.format("0x%08x", get());
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
public class ResXmlString extends StringItem {
|
||||
public ResXmlString(boolean utf8) {
|
||||
super(utf8);
|
||||
}
|
||||
}
|
37
src/main/java/com/reandroid/lib/arsc/item/ShortItem.java
Normal file
37
src/main/java/com/reandroid/lib/arsc/item/ShortItem.java
Normal file
@ -0,0 +1,37 @@
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
public class ShortItem extends BlockItem {
|
||||
private short mCache;
|
||||
|
||||
public ShortItem(){
|
||||
super(2);
|
||||
}
|
||||
public ShortItem(short val){
|
||||
this();
|
||||
set(val);
|
||||
}
|
||||
public void set(short val){
|
||||
if(val==mCache){
|
||||
return;
|
||||
}
|
||||
mCache=val;
|
||||
byte[] bts = getBytesInternal();
|
||||
bts[1]= (byte) (val >>> 8 & 0xff);
|
||||
bts[0]= (byte) (val & 0xff);
|
||||
}
|
||||
public short get(){
|
||||
return mCache;
|
||||
}
|
||||
@Override
|
||||
public void onBytesChanged() {
|
||||
mCache=readShortBytes();
|
||||
}
|
||||
private short readShortBytes(){
|
||||
byte[] bts = getBytesInternal();
|
||||
return (short) (bts[0] & 0xff | (bts[1] & 0xff) << 8);
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return String.valueOf(get());
|
||||
}
|
||||
}
|
11
src/main/java/com/reandroid/lib/arsc/item/SpecString.java
Normal file
11
src/main/java/com/reandroid/lib/arsc/item/SpecString.java
Normal file
@ -0,0 +1,11 @@
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
public class SpecString extends StringItem {
|
||||
public SpecString(boolean utf8) {
|
||||
super(utf8);
|
||||
}
|
||||
@Override
|
||||
StyleItem getStyle(){
|
||||
return null;
|
||||
}
|
||||
}
|
289
src/main/java/com/reandroid/lib/arsc/item/StringItem.java
Normal file
289
src/main/java/com/reandroid/lib/arsc/item/StringItem.java
Normal file
@ -0,0 +1,289 @@
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.pool.BaseStringPool;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class StringItem extends BlockItem {
|
||||
private String mCache;
|
||||
private boolean mUtf8;
|
||||
public StringItem(boolean utf8) {
|
||||
super(0);
|
||||
this.mUtf8=utf8;
|
||||
}
|
||||
public String getHtml(){
|
||||
String str=get();
|
||||
if(str==null){
|
||||
return null;
|
||||
}
|
||||
StyleItem styleItem=getStyle();
|
||||
if(styleItem==null){
|
||||
return str;
|
||||
}
|
||||
return styleItem.applyHtml(str);
|
||||
}
|
||||
public String get(){
|
||||
return mCache;
|
||||
}
|
||||
public void set(String str){
|
||||
String old=get();
|
||||
if(str==null){
|
||||
if(old==null){
|
||||
return;
|
||||
}
|
||||
}else if(str.equals(old)){
|
||||
return;
|
||||
}
|
||||
byte[] bts=encodeString(str);
|
||||
setBytesInternal(bts);
|
||||
}
|
||||
|
||||
public boolean isUtf8(){
|
||||
return mUtf8;
|
||||
}
|
||||
public void setUtf8(boolean utf8){
|
||||
if(utf8==mUtf8){
|
||||
return;
|
||||
}
|
||||
mUtf8=utf8;
|
||||
onBytesChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBytesChanged() {
|
||||
mCache=decodeString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
int len=calculateReadLength(reader);
|
||||
setBytesLength(len, false);
|
||||
byte[] bts=getBytesInternal();
|
||||
reader.readFully(bts);
|
||||
onBytesChanged();
|
||||
}
|
||||
int calculateReadLength(BlockReader reader) throws IOException {
|
||||
byte[] bts=new byte[4];
|
||||
reader.readFully(bts);
|
||||
reader.offset(-4);
|
||||
int[] len;
|
||||
if(isUtf8()){
|
||||
len=decodeUtf8StringByteLength(bts);
|
||||
}else {
|
||||
len=decodeUtf16StringByteLength(bts);
|
||||
}
|
||||
int add=isUtf8()?1:2;
|
||||
return len[0]+len[1]+add;
|
||||
}
|
||||
String decodeString(){
|
||||
return decodeString(getBytesInternal(), mUtf8);
|
||||
}
|
||||
byte[] encodeString(String str){
|
||||
if(mUtf8){
|
||||
return encodeUtf8ToBytes(str);
|
||||
}else {
|
||||
return encodeUtf16ToBytes(str);
|
||||
}
|
||||
}
|
||||
private String decodeString(byte[] allStringBytes, boolean isUtf8) {
|
||||
if(isNullBytes(allStringBytes)){
|
||||
if(allStringBytes==null||allStringBytes.length==0){
|
||||
return null;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
int[] offLen;
|
||||
if(isUtf8){
|
||||
offLen=decodeUtf8StringByteLength(allStringBytes);
|
||||
}else {
|
||||
offLen=decodeUtf16StringByteLength(allStringBytes);
|
||||
}
|
||||
CharsetDecoder charsetDecoder;
|
||||
if(isUtf8){
|
||||
charsetDecoder=UTF8_DECODER;
|
||||
}else {
|
||||
charsetDecoder=UTF16LE_DECODER;
|
||||
}
|
||||
try {
|
||||
ByteBuffer buf=ByteBuffer.wrap(allStringBytes, offLen[0], offLen[1]);
|
||||
CharBuffer charBuffer=charsetDecoder.decode(buf);
|
||||
return charBuffer.toString();
|
||||
} catch (CharacterCodingException ex) {
|
||||
return new String(allStringBytes, offLen[0], offLen[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
StyleItem getStyle(){
|
||||
BaseStringPool stringPool=getStringPool();
|
||||
if(stringPool==null){
|
||||
return null;
|
||||
}
|
||||
int index=getIndex();
|
||||
return stringPool.getStyle(index);
|
||||
}
|
||||
private BaseStringPool getStringPool(){
|
||||
Block parent=getParent();
|
||||
while (parent!=null){
|
||||
if(parent instanceof BaseStringPool){
|
||||
return (BaseStringPool)parent;
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
String str=get();
|
||||
if(str==null){
|
||||
return "NULL";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
private static int[] decodeUtf8StringByteLength(byte[] lengthBytes) {
|
||||
int offset=0;
|
||||
int val = lengthBytes[offset];
|
||||
int length;
|
||||
if ((val & 0x80) != 0) {
|
||||
offset += 2;
|
||||
} else {
|
||||
offset += 1;
|
||||
}
|
||||
val = lengthBytes[offset];
|
||||
offset += 1;
|
||||
if ((val & 0x80) != 0) {
|
||||
int low = (lengthBytes[offset] & 0xFF);
|
||||
length = val & 0x7F;
|
||||
length = length << 8;
|
||||
length = length + low;
|
||||
offset += 1;
|
||||
} else {
|
||||
length = val;
|
||||
}
|
||||
return new int[] { offset, length};
|
||||
}
|
||||
private static int[] decodeUtf16StringByteLength(byte[] lengthBytes) {
|
||||
int val = ((lengthBytes[1] & 0xFF) << 8 | lengthBytes[0] & 0xFF);
|
||||
if ((val & 0x8000) != 0) {
|
||||
int high = (lengthBytes[3] & 0xFF) << 8;
|
||||
int low = (lengthBytes[2] & 0xFF);
|
||||
int len_value = ((val & 0x7FFF) << 16) + (high + low);
|
||||
return new int[] {4, len_value * 2};
|
||||
|
||||
}
|
||||
return new int[] {2, val * 2};
|
||||
}
|
||||
static boolean isNullBytes(byte[] bts){
|
||||
if(bts==null){
|
||||
return true;
|
||||
}
|
||||
int max=bts.length;
|
||||
if(max<2){
|
||||
return true;
|
||||
}
|
||||
for(int i=2; i<max;i++){
|
||||
if(bts[i] != 0){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private static byte[] encodeUtf8ToBytes(String str){
|
||||
byte[] bts=new byte[0];
|
||||
byte[] lenBytes=new byte[2];
|
||||
if(str!=null){
|
||||
bts=str.getBytes(StandardCharsets.UTF_8);
|
||||
int strLen=bts.length;
|
||||
if((strLen & 0xff80)!=0){
|
||||
lenBytes=new byte[4];
|
||||
int l2=strLen&0xff;
|
||||
int l1=(strLen-l2)>>8;
|
||||
lenBytes[3]=(byte) (l2);
|
||||
lenBytes[2]=(byte) (l1|0x80);
|
||||
strLen=str.length();
|
||||
l2=strLen&0xff;
|
||||
l1=(strLen-l2)>>8;
|
||||
lenBytes[1]=(byte) (l2);
|
||||
lenBytes[0]=(byte) (l1|0x80);
|
||||
}else{
|
||||
lenBytes=new ShortItem((short) strLen).getBytesInternal();
|
||||
lenBytes[1]=lenBytes[0];
|
||||
lenBytes[0]=(byte)str.length();
|
||||
}
|
||||
}else {
|
||||
bts=new byte[0];
|
||||
}
|
||||
return addBytes(lenBytes, bts, new byte[1]);
|
||||
}
|
||||
private static byte[] encodeUtf16ToBytes(String str){
|
||||
if(str==null){
|
||||
return null;
|
||||
}
|
||||
byte[] lenBytes;
|
||||
byte[] bts=getUtf16Bytes(str);
|
||||
int strLen=bts.length;
|
||||
strLen=strLen/2;
|
||||
if((strLen & 0xffff8000)!=0){
|
||||
lenBytes=new byte[4];
|
||||
int low=strLen&0xff;
|
||||
int high=(strLen-low)&0xff00;
|
||||
int rem=strLen-low-high;
|
||||
lenBytes[3]=(byte) (high>>8);
|
||||
lenBytes[2]=(byte) (low);
|
||||
low=rem&0xff;
|
||||
high=(rem&0xff00)>>8;
|
||||
lenBytes[1]=(byte) (high|0x80);
|
||||
lenBytes[0]=(byte) (low);
|
||||
}else{
|
||||
lenBytes=new ShortItem((short) strLen).getBytesInternal();
|
||||
}
|
||||
return addBytes(lenBytes, bts, new byte[2]);
|
||||
}
|
||||
static byte[] getUtf16Bytes(String str){
|
||||
return str.getBytes(StandardCharsets.UTF_16LE);
|
||||
}
|
||||
|
||||
private static byte[] addBytes(byte[] bts1, byte[] bts2, byte[] bts3){
|
||||
if(bts1==null && bts2==null && bts3==null){
|
||||
return null;
|
||||
}
|
||||
int len=0;
|
||||
if(bts1!=null){
|
||||
len=bts1.length;
|
||||
}
|
||||
if(bts2!=null){
|
||||
len+=bts2.length;
|
||||
}
|
||||
if(bts3!=null){
|
||||
len+=bts3.length;
|
||||
}
|
||||
byte[] result=new byte[len];
|
||||
int start=0;
|
||||
if(bts1!=null){
|
||||
start=bts1.length;
|
||||
System.arraycopy(bts1, 0, result, 0, start);
|
||||
}
|
||||
if(bts2!=null){
|
||||
System.arraycopy(bts2, 0, result, start, bts2.length);
|
||||
start+=bts2.length;
|
||||
}
|
||||
if(bts3!=null){
|
||||
System.arraycopy(bts3, 0, result, start, bts3.length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private final CharsetDecoder UTF16LE_DECODER = StandardCharsets.UTF_16LE.newDecoder();
|
||||
private final CharsetDecoder UTF8_DECODER = StandardCharsets.UTF_8.newDecoder();
|
||||
}
|
226
src/main/java/com/reandroid/lib/arsc/item/StyleItem.java
Normal file
226
src/main/java/com/reandroid/lib/arsc/item/StyleItem.java
Normal file
@ -0,0 +1,226 @@
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.model.StyleSpanInfo;
|
||||
import com.reandroid.lib.arsc.pool.BaseStringPool;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class StyleItem extends IntegerArray{
|
||||
private List<int[]> mStyleList;
|
||||
public StyleItem() {
|
||||
super();
|
||||
mStyleList=createStyleList();
|
||||
}
|
||||
private void setEndValue(int negOne){
|
||||
super.put(size()-1, negOne);
|
||||
}
|
||||
final Integer getEndValue(){
|
||||
return super.get(size()-1);
|
||||
}
|
||||
final Integer getStringRef(int index){
|
||||
int i=index*STYLE_PIECE_COUNT+STRING_REF;
|
||||
return super.get(i);
|
||||
}
|
||||
final void setStringRef(int index, int val){
|
||||
int i=index*STYLE_PIECE_COUNT+STRING_REF;
|
||||
super.put(i, val);
|
||||
}
|
||||
final Integer getFirstChar(int index){
|
||||
int i=index*STYLE_PIECE_COUNT+CHAR_FIRST;
|
||||
return super.get(i);
|
||||
}
|
||||
final void setFirstChar(int index, int val){
|
||||
int i=index*STYLE_PIECE_COUNT+CHAR_FIRST;
|
||||
super.put(i, val);
|
||||
}
|
||||
final Integer getLastChar(int index){
|
||||
int i=index*STYLE_PIECE_COUNT+CHAR_LAST;
|
||||
return super.get(i);
|
||||
}
|
||||
final void setLastChar(int index, int val){
|
||||
int i=index*STYLE_PIECE_COUNT+CHAR_LAST;
|
||||
super.put(i, val);
|
||||
}
|
||||
final void setStylePiece(int index, int refString, int firstChar, int lastChar){
|
||||
int i=index*STYLE_PIECE_COUNT;
|
||||
super.put(i+STRING_REF, refString);
|
||||
super.put(i+CHAR_FIRST, firstChar);
|
||||
super.put(i+CHAR_LAST, lastChar);
|
||||
}
|
||||
final int[] getStylePiece(int index){
|
||||
if(index<0||index>= getStylePieceCount()){
|
||||
return null;
|
||||
}
|
||||
int[] result=new int[STYLE_PIECE_COUNT];
|
||||
int i=index*STYLE_PIECE_COUNT;
|
||||
result[STRING_REF]=super.get(i);
|
||||
result[CHAR_FIRST]=super.get(i+CHAR_FIRST);
|
||||
result[CHAR_LAST]=super.get(i+CHAR_LAST);
|
||||
return result;
|
||||
}
|
||||
final void setStylePiece(int index, int[] three){
|
||||
if(three==null || three.length<STYLE_PIECE_COUNT){
|
||||
return;
|
||||
}
|
||||
int i=index*STYLE_PIECE_COUNT;
|
||||
super.put(i+STRING_REF, three[STRING_REF]);
|
||||
super.put(i+CHAR_FIRST, three[CHAR_FIRST]);
|
||||
super.put(i+CHAR_LAST, three[CHAR_LAST]);
|
||||
}
|
||||
final void ensureStylePieceCount(int count){
|
||||
if(count<0){
|
||||
count=0;
|
||||
}
|
||||
if(count<getStylePieceCount()){
|
||||
setStylePieceCount(count);
|
||||
}
|
||||
}
|
||||
final int getStylePieceCount(){
|
||||
int sz=size()-1;
|
||||
if(sz<0){
|
||||
sz=0;
|
||||
}
|
||||
return sz/STYLE_PIECE_COUNT;
|
||||
}
|
||||
final void setStylePieceCount(int count){
|
||||
if(count<0){
|
||||
count=0;
|
||||
}
|
||||
int cur= getStylePieceCount();
|
||||
if(count==cur){
|
||||
return;
|
||||
}
|
||||
int max=count*STYLE_PIECE_COUNT+1;
|
||||
if(size()==0 || count==0){
|
||||
super.setSize(max);
|
||||
setEndValue(END_VALUE);
|
||||
return;
|
||||
}
|
||||
List<int[]> copy=new ArrayList<>(mStyleList);
|
||||
Integer end= getEndValue();
|
||||
if(end==null){
|
||||
end=END_VALUE;
|
||||
}
|
||||
super.setSize(max);
|
||||
max=count;
|
||||
int copyMax=copy.size();
|
||||
if(copyMax>max){
|
||||
copyMax=max;
|
||||
}
|
||||
for(int i=0;i<copyMax;i++){
|
||||
int[] val=copy.get(i);
|
||||
setStylePiece(i, val);
|
||||
}
|
||||
setEndValue(end);
|
||||
}
|
||||
final List<int[]> getStyleList(){
|
||||
return mStyleList;
|
||||
}
|
||||
private List<int[]> createStyleList(){
|
||||
List<int[]> results=new ArrayList<>();
|
||||
int max=getStylePieceCount();
|
||||
for(int i=0;i<max;i++){
|
||||
results.add(getStylePiece(i));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
final List<StyleSpanInfo> getSpanInfo(){
|
||||
BaseStringPool stringPool= getStringPool();
|
||||
if(stringPool==null){
|
||||
return null;
|
||||
}
|
||||
List<int[]> allPiece=getStyleList();
|
||||
List<StyleSpanInfo> results=new ArrayList<>();
|
||||
for(int[] piece:allPiece){
|
||||
StringItem stringItem=stringPool.get(piece[STRING_REF]);
|
||||
if(stringItem==null){
|
||||
return null;
|
||||
}
|
||||
StyleSpanInfo info=new StyleSpanInfo(stringItem.get(), piece[CHAR_FIRST], piece[CHAR_LAST]);
|
||||
results.add(info);
|
||||
}
|
||||
if(results.size()==0){
|
||||
return null;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
private BaseStringPool getStringPool(){
|
||||
Block parent=getParent();
|
||||
while (parent!=null){
|
||||
if(parent instanceof BaseStringPool){
|
||||
return (BaseStringPool)parent;
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String applyHtml(String str){
|
||||
if(str==null){
|
||||
return null;
|
||||
}
|
||||
List<StyleSpanInfo> allInfo=getSpanInfo();
|
||||
if(allInfo==null){
|
||||
return str;
|
||||
}
|
||||
StringBuilder builder=new StringBuilder();
|
||||
char[] allChars=str.toCharArray();
|
||||
int max=allChars.length;
|
||||
for(int i=0;i<max;i++){
|
||||
char ch=allChars[i];
|
||||
boolean lastAppend=false;
|
||||
for(StyleSpanInfo info:allInfo){
|
||||
boolean isLast=(info.LAST==i);
|
||||
if(info.FIRST==i || isLast){
|
||||
if(isLast && !lastAppend){
|
||||
builder.append(ch);
|
||||
lastAppend=true;
|
||||
}
|
||||
if(isLast){
|
||||
builder.append(info.getEndTag());
|
||||
}else {
|
||||
builder.append(info.getStartTag());
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!lastAppend){
|
||||
builder.append(ch);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
@Override
|
||||
public void onBytesChanged() {
|
||||
mStyleList=createStyleList();
|
||||
}
|
||||
@Override
|
||||
public void setNull(boolean is_null){
|
||||
if(!is_null){
|
||||
return;
|
||||
}
|
||||
setStylePieceCount(0);
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
int nextPos=reader.searchNextIntPosition(4, END_VALUE);
|
||||
if(nextPos<0){
|
||||
return;
|
||||
}
|
||||
int len=nextPos-reader.getPosition()+4;
|
||||
super.setBytesLength(len, false);
|
||||
byte[] bts=getBytesInternal();
|
||||
reader.readFully(bts);
|
||||
onBytesChanged();
|
||||
}
|
||||
private static final int STRING_REF=0;
|
||||
private static final int CHAR_FIRST=1;
|
||||
private static final int CHAR_LAST=2;
|
||||
|
||||
private static final int STYLE_PIECE_COUNT=3;
|
||||
|
||||
private static final int END_VALUE=0xFFFFFFFF;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
public class TableString extends StringItem {
|
||||
public TableString(boolean utf8) {
|
||||
super(utf8);
|
||||
}
|
||||
}
|
15
src/main/java/com/reandroid/lib/arsc/item/TypeString.java
Normal file
15
src/main/java/com/reandroid/lib/arsc/item/TypeString.java
Normal file
@ -0,0 +1,15 @@
|
||||
package com.reandroid.lib.arsc.item;
|
||||
|
||||
|
||||
public class TypeString extends StringItem {
|
||||
public TypeString(boolean utf8) {
|
||||
super(utf8);
|
||||
}
|
||||
public int getId(){
|
||||
return getIndex()+1;
|
||||
}
|
||||
@Override
|
||||
StyleItem getStyle(){
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.reandroid.lib.arsc.model;
|
||||
|
||||
public class StyleSpanInfo {
|
||||
public final String TAG;
|
||||
public final int FIRST;
|
||||
public final int LAST;
|
||||
public StyleSpanInfo(String tag, int first, int last){
|
||||
this.TAG=tag;
|
||||
this.FIRST=first;
|
||||
this.LAST=last;
|
||||
}
|
||||
public String getStartTag(){
|
||||
int i=TAG.indexOf(';');
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append('<');
|
||||
if(i<0){
|
||||
builder.append(TAG);
|
||||
}else {
|
||||
builder.append(TAG, 0, i);
|
||||
builder.append(' ');
|
||||
builder.append(TAG.substring(i+1));
|
||||
}
|
||||
builder.append('>');
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public String getEndTag(){
|
||||
int i=TAG.indexOf(';');
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append('<');
|
||||
builder.append('/');
|
||||
if(i<0){
|
||||
builder.append(TAG);
|
||||
}else {
|
||||
builder.append(TAG, 0, i);
|
||||
}
|
||||
builder.append('>');
|
||||
return builder.toString();
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return TAG+" ("+FIRST+", "+LAST+")";
|
||||
}
|
||||
}
|
197
src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java
Normal file
197
src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java
Normal file
@ -0,0 +1,197 @@
|
||||
package com.reandroid.lib.arsc.pool;
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
import com.reandroid.lib.arsc.array.StringArray;
|
||||
import com.reandroid.lib.arsc.array.StyleArray;
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.chunk.BaseChunk;
|
||||
import com.reandroid.lib.arsc.io.BlockLoad;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public abstract class BaseStringPool<T extends StringItem> extends BaseChunk implements BlockLoad {
|
||||
private final IntegerItem mCountStrings;
|
||||
private final IntegerItem mCountStyles;
|
||||
private final ShortItem mFlagUtf8;
|
||||
private final ShortItem mFlagSorted;
|
||||
private final IntegerItem mStartStrings;
|
||||
private final IntegerItem mStartStyles;
|
||||
private final IntegerArray mOffsetStrings;
|
||||
private final IntegerArray mOffsetStyles;
|
||||
private final StringArray<T> mArrayStrings;
|
||||
private final StyleArray mArrayStyles;
|
||||
|
||||
private final Map<String, StringGroup<T>> mUniqueMap;
|
||||
|
||||
|
||||
BaseStringPool(boolean is_utf8){
|
||||
super(ChunkType.STRING, 4);
|
||||
|
||||
this.mCountStrings=new IntegerItem(); //header
|
||||
this.mCountStyles=new IntegerItem(); //header
|
||||
this.mFlagUtf8 =new ShortItem(); //header
|
||||
this.mFlagSorted=new ShortItem(); //header
|
||||
this.mStartStrings=new IntegerItem(); //header
|
||||
this.mStartStyles=new IntegerItem(); //header
|
||||
|
||||
this.mOffsetStrings=new IntegerArray();//1
|
||||
this.mOffsetStyles=new IntegerArray(); //2
|
||||
this.mArrayStrings=newInstance(mOffsetStrings, mCountStrings, mStartStrings, is_utf8); //3
|
||||
this.mArrayStyles=new StyleArray(mOffsetStyles, mCountStyles, mStartStyles); //4
|
||||
|
||||
addToHeader(mCountStrings);
|
||||
addToHeader(mCountStyles);
|
||||
addToHeader(mFlagUtf8);
|
||||
addToHeader(mFlagSorted);
|
||||
addToHeader(mStartStrings);
|
||||
addToHeader(mStartStyles);
|
||||
|
||||
addChild(mOffsetStrings);
|
||||
addChild(mOffsetStyles);
|
||||
addChild(mArrayStrings);
|
||||
addChild(mArrayStyles);
|
||||
|
||||
setUtf8(is_utf8, false);
|
||||
|
||||
mFlagUtf8.setBlockLoad(this);
|
||||
|
||||
mUniqueMap=new HashMap<>();
|
||||
|
||||
}
|
||||
public boolean contains(String str){
|
||||
return mUniqueMap.containsKey(str);
|
||||
}
|
||||
public final T get(int index){
|
||||
return mArrayStrings.get(index);
|
||||
}
|
||||
public final StringGroup<T> get(String str){
|
||||
return mUniqueMap.get(str);
|
||||
}
|
||||
public final T getOrCreate(String str){
|
||||
StringGroup<T> group=getOrCreateGroup(str);
|
||||
T[] items=group.getItems();
|
||||
if(items.length==0){
|
||||
T t=createNewString(str);
|
||||
group.add(t);
|
||||
items=group.getItems();
|
||||
}
|
||||
return items[0];
|
||||
}
|
||||
private StringGroup<T> getOrCreateGroup(String str){
|
||||
StringGroup<T> group=get(str);
|
||||
if(group!=null){
|
||||
return group;
|
||||
}
|
||||
T[] items=mArrayStrings.newInstance(0);
|
||||
group=new StringGroup<>(mArrayStrings, str, items);
|
||||
mUniqueMap.put(str, group);
|
||||
return group;
|
||||
}
|
||||
private T createNewString(String str){
|
||||
T item=mArrayStrings.createNext();
|
||||
item.set(str);
|
||||
mCountStrings.set(mArrayStrings.childesCount());
|
||||
return item;
|
||||
}
|
||||
private void reUpdateUniqueMap(){
|
||||
mUniqueMap.clear();
|
||||
T[] allChildes=getStrings();
|
||||
if(allChildes==null){
|
||||
return;
|
||||
}
|
||||
int max=allChildes.length;
|
||||
for(int i=0;i<max;i++){
|
||||
T item=allChildes[i];
|
||||
if(item==null){
|
||||
continue;
|
||||
}
|
||||
String str=item.get();
|
||||
if(str==null){
|
||||
continue;
|
||||
}
|
||||
updateUniqueMap(item);
|
||||
}
|
||||
}
|
||||
private void updateUniqueMap(T item){
|
||||
String str=item.get();
|
||||
StringGroup<T> group= getOrCreateGroup(str);
|
||||
group.add(item);
|
||||
}
|
||||
public final StyleItem getStyle(int index){
|
||||
return mArrayStyles.get(index);
|
||||
}
|
||||
public final int countStrings(){
|
||||
return mArrayStrings.childesCount();
|
||||
}
|
||||
public final int countStyles(){
|
||||
return mArrayStyles.childesCount();
|
||||
}
|
||||
|
||||
public final T[] getStrings(){
|
||||
return mArrayStrings.getChildes();
|
||||
}
|
||||
public final StyleItem[] getStyles(){
|
||||
return mArrayStyles.getChildes();
|
||||
}
|
||||
private void setUtf8Flag(short flag){
|
||||
mFlagUtf8.set(flag);
|
||||
}
|
||||
public void setUtf8(boolean is_utf8){
|
||||
setUtf8(is_utf8, true);
|
||||
}
|
||||
private void setSortedFlag(short flag){
|
||||
mFlagSorted.set(flag);
|
||||
}
|
||||
public final void setSorted(boolean sorted){
|
||||
if(sorted){
|
||||
setSortedFlag(FLAG_SORTED);
|
||||
}else {
|
||||
setSortedFlag((short)0);
|
||||
}
|
||||
}
|
||||
private void setUtf8(boolean is_utf8, boolean updateAll){
|
||||
boolean old= isUtf8Flag();
|
||||
if(is_utf8){
|
||||
setUtf8Flag(UTF8_FLAG_VALUE);
|
||||
}else {
|
||||
setUtf8Flag((short) 0);
|
||||
}
|
||||
if(!updateAll || old == isUtf8Flag()){
|
||||
return;
|
||||
}
|
||||
mArrayStrings.setUtf8(is_utf8);
|
||||
}
|
||||
private boolean isUtf8Flag(){
|
||||
return (mFlagUtf8.get() & FLAG_UTF8) !=0;
|
||||
}
|
||||
private boolean isSortedFlag(){
|
||||
return (mFlagSorted.get() & FLAG_SORTED) !=0;
|
||||
}
|
||||
|
||||
|
||||
abstract StringArray<T> newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8);
|
||||
@Override
|
||||
protected void onChunkRefreshed() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public void onChunkLoaded() {
|
||||
reUpdateUniqueMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockLoaded(BlockReader reader, Block sender) throws IOException {
|
||||
if(sender== mFlagUtf8){
|
||||
mArrayStrings.setUtf8(isUtf8Flag());
|
||||
}
|
||||
}
|
||||
private static final short UTF8_FLAG_VALUE=0x0100;
|
||||
|
||||
private static final short FLAG_UTF8 = 0x0100;
|
||||
private static final short FLAG_SORTED = 0x0100;
|
||||
}
|
8
src/main/java/com/reandroid/lib/arsc/pool/PoolType.java
Normal file
8
src/main/java/com/reandroid/lib/arsc/pool/PoolType.java
Normal file
@ -0,0 +1,8 @@
|
||||
package com.reandroid.lib.arsc.pool;
|
||||
|
||||
public enum PoolType {
|
||||
TABLE,
|
||||
SPEC,
|
||||
TYPE,
|
||||
XML
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.reandroid.lib.arsc.pool;
|
||||
|
||||
import com.reandroid.lib.arsc.array.StringArray;
|
||||
import com.reandroid.lib.arsc.array.ResXmlStringArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.ResXmlString;
|
||||
|
||||
public class ResXmlStringPool extends BaseStringPool<ResXmlString> {
|
||||
public ResXmlStringPool(boolean is_utf8) {
|
||||
super(is_utf8);
|
||||
}
|
||||
@Override
|
||||
StringArray<ResXmlString> newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
|
||||
return new ResXmlStringArray(offsets, itemCount, itemStart, is_utf8);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.reandroid.lib.arsc.pool;
|
||||
|
||||
import com.reandroid.lib.arsc.array.SpecStringArray;
|
||||
import com.reandroid.lib.arsc.array.StringArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.SpecString;
|
||||
|
||||
public class SpecStringPool extends BaseStringPool<SpecString> {
|
||||
public SpecStringPool(boolean is_utf8){
|
||||
super(is_utf8);
|
||||
}
|
||||
|
||||
@Override
|
||||
StringArray<SpecString> newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
|
||||
return new SpecStringArray(offsets, itemCount, itemStart, is_utf8);
|
||||
}
|
||||
}
|
105
src/main/java/com/reandroid/lib/arsc/pool/StringGroup.java
Normal file
105
src/main/java/com/reandroid/lib/arsc/pool/StringGroup.java
Normal file
@ -0,0 +1,105 @@
|
||||
package com.reandroid.lib.arsc.pool;
|
||||
|
||||
import com.reandroid.lib.arsc.base.BlockArrayCreator;
|
||||
import com.reandroid.lib.arsc.item.StringItem;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
|
||||
public class StringGroup<T extends StringItem> {
|
||||
private final BlockArrayCreator<T> mBlockArrayCreator;
|
||||
private final String name;
|
||||
private T[] items;
|
||||
private final int hashCode;
|
||||
StringGroup(BlockArrayCreator<T> blockArrayCreator, String name, T[] items){
|
||||
this.mBlockArrayCreator=blockArrayCreator;
|
||||
this.name=name;
|
||||
this.items=items;
|
||||
this.hashCode=(getClass().getName()+"-"+name).hashCode();
|
||||
}
|
||||
public boolean contains(T strItem){
|
||||
if(strItem==null){
|
||||
return false;
|
||||
}
|
||||
int len=items.length;
|
||||
for(int i=0;i<len;i++){
|
||||
if(strItem==items[i]){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public void remove(T strItem){
|
||||
if(strItem==null){
|
||||
return;
|
||||
}
|
||||
boolean found=false;
|
||||
int len=items.length;
|
||||
for(int i=0;i<len;i++){
|
||||
if(strItem==items[i]){
|
||||
items[i]=null;
|
||||
found=true;
|
||||
}
|
||||
}
|
||||
if(found){
|
||||
trimToSize();
|
||||
}
|
||||
}
|
||||
public void add(T strItem){
|
||||
if(strItem==null){
|
||||
return;
|
||||
}
|
||||
int index=items.length;
|
||||
T[] update=createNew(index+1);
|
||||
System.arraycopy(items, 0, update, 0, index);
|
||||
update[index]=strItem;
|
||||
items=update;
|
||||
}
|
||||
public T[] getItems(){
|
||||
return items;
|
||||
}
|
||||
private void trimToSize(){
|
||||
int count=countNonNull();
|
||||
int len=items.length;
|
||||
if(count==len){
|
||||
return;
|
||||
}
|
||||
T[] update=createNew(count);
|
||||
int index=0;
|
||||
for(int i=0;i<len;i++){
|
||||
T block=items[i];
|
||||
if(block!=null){
|
||||
update[index]=block;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
items=update;
|
||||
}
|
||||
private int countNonNull(){
|
||||
int result=0;
|
||||
for(T t:items){
|
||||
if(t!=null){
|
||||
result++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private T[] createNew(int len){
|
||||
return mBlockArrayCreator.newInstance(len);
|
||||
}
|
||||
@Override
|
||||
public int hashCode(){
|
||||
return hashCode;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj){
|
||||
if(obj instanceof StringGroup){
|
||||
StringGroup other=(StringGroup)obj;
|
||||
return name.equals(other.name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return items.length+"{"+name+"}";
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.reandroid.lib.arsc.pool;
|
||||
|
||||
import com.reandroid.lib.arsc.array.StringArray;
|
||||
import com.reandroid.lib.arsc.array.TableStringArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.TableString;
|
||||
|
||||
public class TableStringPool extends BaseStringPool<TableString> {
|
||||
public TableStringPool(boolean is_utf8) {
|
||||
super(is_utf8);
|
||||
}
|
||||
@Override
|
||||
StringArray<TableString> newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
|
||||
return new TableStringArray(offsets, itemCount, itemStart, is_utf8);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.reandroid.lib.arsc.pool;
|
||||
|
||||
import com.reandroid.lib.arsc.array.StringArray;
|
||||
import com.reandroid.lib.arsc.array.TypeStringArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.TypeString;
|
||||
|
||||
public class TypeStringPool extends BaseStringPool<TypeString> {
|
||||
public TypeStringPool(boolean is_utf8) {
|
||||
super(is_utf8);
|
||||
}
|
||||
public TypeString getById(int id){
|
||||
return super.get(id-1);
|
||||
}
|
||||
@Override
|
||||
StringArray<TypeString> newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
|
||||
return new TypeStringArray(offsets, itemCount, itemStart, is_utf8);
|
||||
}
|
||||
}
|
95
src/main/java/com/reandroid/lib/arsc/value/BaseResValue.java
Normal file
95
src/main/java/com/reandroid/lib/arsc/value/BaseResValue.java
Normal file
@ -0,0 +1,95 @@
|
||||
package com.reandroid.lib.arsc.value;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.item.BlockItem;
|
||||
|
||||
public abstract class BaseResValue extends BlockItem {
|
||||
BaseResValue(int bytesLength){
|
||||
super(bytesLength);
|
||||
}
|
||||
|
||||
public ValueType getValueType(){
|
||||
return ValueType.valueOf(getType());
|
||||
}
|
||||
public EntryBlock getEntryBlock(){
|
||||
Block parent=getParent();
|
||||
while(parent!=null){
|
||||
if(parent instanceof EntryBlock){
|
||||
return (EntryBlock)parent;
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setType(ValueType valueType){
|
||||
byte type=0;
|
||||
if(valueType!=null){
|
||||
type=valueType.getByte();
|
||||
}
|
||||
setType(type);
|
||||
}
|
||||
|
||||
abstract void setHeaderSize(short size);
|
||||
abstract short getHeaderSize();
|
||||
|
||||
abstract void setReserved(byte reserved);
|
||||
abstract byte getReserved();
|
||||
|
||||
public abstract void setType(byte type);
|
||||
public abstract byte getType();
|
||||
public abstract int getData();
|
||||
public abstract void setData(int data);
|
||||
|
||||
@Override
|
||||
public void onBytesChanged() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
int getInt(int offset){
|
||||
byte[] bts = getBytesInternal();
|
||||
return bts[offset] & 0xff |
|
||||
(bts[offset+1] & 0xff) << 8 |
|
||||
(bts[offset+2] & 0xff) << 16 |
|
||||
(bts[offset+3] & 0xff) << 24;
|
||||
}
|
||||
void setInt(int offset, int val){
|
||||
if(val==getInt(offset)){
|
||||
return;
|
||||
}
|
||||
byte[] bts = getBytesInternal();
|
||||
bts[offset+3]= (byte) (val >>> 24 & 0xff);
|
||||
bts[offset+2]= (byte) (val >>> 16 & 0xff);
|
||||
bts[offset+1]= (byte) (val >>> 8 & 0xff);
|
||||
bts[offset]= (byte) (val & 0xff);
|
||||
onBytesChanged();
|
||||
}
|
||||
|
||||
void setShort(int offset, short val){
|
||||
if(val==getShort(offset)){
|
||||
return;
|
||||
}
|
||||
byte[] bts = getBytesInternal();
|
||||
bts[offset+1]= (byte) (val >>> 8 & 255);
|
||||
bts[offset]= (byte) (val & 255);
|
||||
onBytesChanged();
|
||||
}
|
||||
short getShort(int offset){
|
||||
byte[] bts=getBytesInternal();
|
||||
int i= bts[offset] & 0xff |
|
||||
(bts[offset+1] & 0xff) << 8 ;
|
||||
return (short)i;
|
||||
}
|
||||
void setByte(int offset, byte b){
|
||||
byte[] bts=getBytesInternal();
|
||||
if(b==bts[offset]){
|
||||
return;
|
||||
}
|
||||
bts[offset]=b;
|
||||
onBytesChanged();
|
||||
}
|
||||
byte getByte(int offset){
|
||||
return getBytesInternal()[offset];
|
||||
}
|
||||
}
|
332
src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java
Normal file
332
src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java
Normal file
@ -0,0 +1,332 @@
|
||||
package com.reandroid.lib.arsc.value;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockCounter;
|
||||
import com.reandroid.lib.arsc.chunk.PackageBlock;
|
||||
import com.reandroid.lib.arsc.chunk.TypeBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.ShortItem;
|
||||
import com.reandroid.lib.arsc.item.SpecString;
|
||||
import com.reandroid.lib.arsc.item.TypeString;
|
||||
import com.reandroid.lib.arsc.pool.SpecStringPool;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class EntryBlock extends Block {
|
||||
private ShortItem mHeaderSize;
|
||||
private ShortItem mFlags;
|
||||
private IntegerItem mSpecReference;
|
||||
private BaseResValue mResValue;
|
||||
private boolean mUnLocked;
|
||||
public EntryBlock() {
|
||||
super();
|
||||
}
|
||||
|
||||
public short getFlags(){
|
||||
return mFlags.get();
|
||||
}
|
||||
public void setFlags(short sh){
|
||||
mFlags.set(sh);
|
||||
}
|
||||
public int getSpecReference(){
|
||||
return mSpecReference.get();
|
||||
}
|
||||
public void setSpecReference(int ref){
|
||||
mSpecReference.set(ref);
|
||||
}
|
||||
public BaseResValue getResValue(){
|
||||
return mResValue;
|
||||
}
|
||||
public void setResValue(BaseResValue resValue){
|
||||
if(resValue==mResValue){
|
||||
return;
|
||||
}
|
||||
if(resValue!=null){
|
||||
resValue.setIndex(3);
|
||||
resValue.setParent(this);
|
||||
}
|
||||
if(mResValue!=null){
|
||||
mResValue.setIndex(-1);
|
||||
mResValue.setParent(null);
|
||||
}
|
||||
mResValue=resValue;
|
||||
}
|
||||
|
||||
public void setFlagComplex(boolean is_complex){
|
||||
if(is_complex){
|
||||
setFlags(FLAG_COMPLEX);
|
||||
}else {
|
||||
setFlags(FLAG_INT);
|
||||
}
|
||||
refreshHeaderSize();
|
||||
refreshResValue();
|
||||
}
|
||||
|
||||
public ResConfig getResConfig(){
|
||||
TypeBlock typeBlock=getTypeBlock();
|
||||
if(typeBlock!=null){
|
||||
return typeBlock.getResConfig();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public SpecString getSpecString(){
|
||||
TypeBlock typeBlock=getTypeBlock();
|
||||
if(typeBlock==null){
|
||||
return null;
|
||||
}
|
||||
PackageBlock packageBlock=typeBlock.getPackageBlock();
|
||||
if(packageBlock==null){
|
||||
return null;
|
||||
}
|
||||
SpecStringPool specStringPool=packageBlock.getSpecStringPool();
|
||||
return specStringPool.get(getSpecReference());
|
||||
}
|
||||
public TypeString getTypeString(){
|
||||
TypeBlock typeBlock=getTypeBlock();
|
||||
if(typeBlock!=null){
|
||||
return typeBlock.getTypeString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public String getResourceName(){
|
||||
return getResourceName("@", null);
|
||||
}
|
||||
public String getResourceName(String prefix){
|
||||
return getResourceName(prefix, null);
|
||||
}
|
||||
public String getResourceName(String prefix, String appName){
|
||||
if(isNull()){
|
||||
return null;
|
||||
}
|
||||
TypeString type=getTypeString();
|
||||
if(type==null){
|
||||
return null;
|
||||
}
|
||||
SpecString spec=getSpecString();
|
||||
if(spec==null){
|
||||
return null;
|
||||
}
|
||||
StringBuilder builder=new StringBuilder();
|
||||
if(prefix!=null){
|
||||
builder.append(prefix);
|
||||
}
|
||||
if(appName!=null){
|
||||
builder.append(appName);
|
||||
}
|
||||
builder.append(type.get());
|
||||
builder.append('/');
|
||||
builder.append(spec.get());
|
||||
return builder.toString();
|
||||
}
|
||||
public int getResourceId(){
|
||||
TypeBlock typeBlock=getTypeBlock();
|
||||
if(typeBlock==null){
|
||||
return 0;
|
||||
}
|
||||
PackageBlock packageBlock=typeBlock.getPackageBlock();
|
||||
if(packageBlock==null){
|
||||
return 0;
|
||||
}
|
||||
int pkgId=packageBlock.getId();
|
||||
int typeId=typeBlock.getTypeId();
|
||||
int entryId=getIndex();
|
||||
return ((pkgId << 24) | (typeId << 16) | entryId);
|
||||
}
|
||||
public TypeBlock getTypeBlock(){
|
||||
Block parent=getParent();
|
||||
while (parent!=null){
|
||||
if(parent instanceof TypeBlock){
|
||||
return (TypeBlock)parent;
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private void unlockEntry(){
|
||||
if(mUnLocked){
|
||||
return;
|
||||
}
|
||||
mUnLocked =true;
|
||||
this.mHeaderSize =new ShortItem();
|
||||
this.mFlags =new ShortItem();
|
||||
this.mSpecReference = new IntegerItem();
|
||||
|
||||
mHeaderSize.setIndex(0);
|
||||
mFlags.setIndex(1);
|
||||
mSpecReference.setIndex(2);
|
||||
|
||||
mHeaderSize.setParent(this);
|
||||
mFlags.setParent(this);
|
||||
mSpecReference.setParent(this);
|
||||
|
||||
}
|
||||
private void lockEntry(){
|
||||
if(!mUnLocked){
|
||||
return;
|
||||
}
|
||||
mUnLocked =false;
|
||||
mHeaderSize.setParent(null);
|
||||
mFlags.setParent(null);
|
||||
mSpecReference.setParent(null);
|
||||
|
||||
mHeaderSize.setIndex(-1);
|
||||
mFlags.setIndex(-1);
|
||||
mSpecReference.setIndex(-1);
|
||||
removeResValue();
|
||||
|
||||
this.mHeaderSize =null;
|
||||
this.mFlags =null;
|
||||
this.mSpecReference =null;
|
||||
}
|
||||
private void removeResValue(){
|
||||
if(mResValue!=null){
|
||||
mResValue.setParent(null);
|
||||
mResValue.setIndex(-1);
|
||||
mResValue=null;
|
||||
}
|
||||
}
|
||||
private void refreshResValue(){
|
||||
if(mResValue==null){
|
||||
return;
|
||||
}
|
||||
if(isFlagsComplex()==(mResValue instanceof ResValueBag)){
|
||||
return;
|
||||
}
|
||||
removeResValue();
|
||||
createResValue();
|
||||
}
|
||||
private void refreshHeaderSize(){
|
||||
if(isFlagsComplex()){
|
||||
mHeaderSize.set(HEADER_COMPLEX);
|
||||
}else {
|
||||
mHeaderSize.set(HEADER_INT);
|
||||
}
|
||||
}
|
||||
private void createResValue(){
|
||||
if(getResValue()!=null){
|
||||
return;
|
||||
}
|
||||
BaseResValue resValue;
|
||||
if(isFlagsComplex()){
|
||||
resValue=new ResValueBag();
|
||||
}else {
|
||||
resValue=new ResValueInt();
|
||||
}
|
||||
setResValue(resValue);
|
||||
}
|
||||
|
||||
private boolean isFlagsComplex(){
|
||||
return ((mFlags.get() & FLAG_COMPLEX_MASK) != 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNull(boolean is_null){
|
||||
if(is_null){
|
||||
lockEntry();
|
||||
}else {
|
||||
unlockEntry();
|
||||
}
|
||||
super.setNull(is_null);
|
||||
}
|
||||
@Override
|
||||
public boolean isNull(){
|
||||
if(super.isNull() || !mUnLocked){
|
||||
return true;
|
||||
}
|
||||
return mResValue==null;
|
||||
}
|
||||
@Override
|
||||
public byte[] getBytes() {
|
||||
if(isNull()){
|
||||
return null;
|
||||
}
|
||||
byte[] results=mHeaderSize.getBytes();
|
||||
results=addBytes(results, mFlags.getBytes());
|
||||
results=addBytes(results, mSpecReference.getBytes());
|
||||
results=addBytes(results, mResValue.getBytes());
|
||||
return results;
|
||||
}
|
||||
@Override
|
||||
public int countBytes() {
|
||||
if(isNull()){
|
||||
return 0;
|
||||
}
|
||||
return 8+mResValue.countBytes();
|
||||
}
|
||||
@Override
|
||||
public void onCountUpTo(BlockCounter counter) {
|
||||
if(counter.FOUND){
|
||||
return;
|
||||
}
|
||||
if(counter.END==this){
|
||||
counter.FOUND=true;
|
||||
return;
|
||||
}
|
||||
if(isNull()){
|
||||
return;
|
||||
}
|
||||
counter.addCount(countBytes());
|
||||
mHeaderSize.onCountUpTo(counter);
|
||||
mFlags.onCountUpTo(counter);
|
||||
mSpecReference.onCountUpTo(counter);
|
||||
mResValue.onCountUpTo(counter);
|
||||
}
|
||||
@Override
|
||||
protected int onWriteBytes(OutputStream stream) throws IOException {
|
||||
if(isNull()){
|
||||
return 0;
|
||||
}
|
||||
int result=mHeaderSize.writeBytes(stream);
|
||||
result+=mFlags.writeBytes(stream);
|
||||
result+= mSpecReference.writeBytes(stream);
|
||||
result+=mResValue.writeBytes(stream);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException{
|
||||
setNull(false);
|
||||
removeResValue();
|
||||
mHeaderSize.readBytes(reader);
|
||||
mFlags.readBytes(reader);
|
||||
mSpecReference.readBytes(reader);
|
||||
createResValue();
|
||||
mResValue.readBytes(reader);
|
||||
}
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(getClass().getSimpleName());
|
||||
builder.append(": ");
|
||||
ResConfig resConfig=getResConfig();
|
||||
if(resConfig!=null){
|
||||
builder.append(resConfig.toString());
|
||||
builder.append(", ");
|
||||
}
|
||||
builder.append(" resId=");
|
||||
builder.append(String.format("0x%08x", getResourceId()));
|
||||
if(isNull()){
|
||||
builder.append(", null entry");
|
||||
return builder.toString();
|
||||
}
|
||||
String name=getResourceName();
|
||||
if(name!=null){
|
||||
builder.append('(');
|
||||
builder.append(name);
|
||||
builder.append(')');
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private final static short FLAG_COMPLEX_MASK = 0x0001;
|
||||
|
||||
private final static short FLAG_COMPLEX = 0x0003;
|
||||
private final static short FLAG_INT = 0x0002;
|
||||
|
||||
private final static short HEADER_COMPLEX=0x0010;
|
||||
private final static short HEADER_INT=0x0008;
|
||||
}
|
91
src/main/java/com/reandroid/lib/arsc/value/LibraryInfo.java
Normal file
91
src/main/java/com/reandroid/lib/arsc/value/LibraryInfo.java
Normal file
@ -0,0 +1,91 @@
|
||||
package com.reandroid.lib.arsc.value;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockCounter;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.PackageName;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class LibraryInfo extends Block {
|
||||
private final IntegerItem mPackageId;
|
||||
private final PackageName mPackageName;
|
||||
|
||||
public LibraryInfo(){
|
||||
super();
|
||||
this.mPackageId=new IntegerItem();
|
||||
this.mPackageName=new PackageName();
|
||||
mPackageId.setIndex(0);
|
||||
mPackageId.setParent(this);
|
||||
mPackageName.setIndex(1);
|
||||
mPackageName.setParent(this);
|
||||
}
|
||||
|
||||
public int getPackageId(){
|
||||
return mPackageId.get();
|
||||
}
|
||||
public void setPackageId(int id){
|
||||
mPackageId.set(id);
|
||||
}
|
||||
public String getPackageName(){
|
||||
return mPackageName.get();
|
||||
}
|
||||
public void setPackageName(String packageName){
|
||||
mPackageName.set(packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() {
|
||||
if(isNull()){
|
||||
return null;
|
||||
}
|
||||
return addBytes(mPackageId.getBytes(), mPackageName.getBytes());
|
||||
}
|
||||
@Override
|
||||
public int countBytes() {
|
||||
if(isNull()){
|
||||
return 0;
|
||||
}
|
||||
return mPackageId.countBytes()+mPackageName.countBytes();
|
||||
}
|
||||
@Override
|
||||
public void onCountUpTo(BlockCounter counter) {
|
||||
if(counter.FOUND){
|
||||
return;
|
||||
}
|
||||
if(counter.END==this){
|
||||
counter.FOUND=true;
|
||||
return;
|
||||
}
|
||||
mPackageId.onCountUpTo(counter);
|
||||
mPackageName.onCountUpTo(counter);
|
||||
}
|
||||
@Override
|
||||
protected int onWriteBytes(OutputStream stream) throws IOException {
|
||||
int result=mPackageId.writeBytes(stream);
|
||||
result+=mPackageName.writeBytes(stream);
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException{
|
||||
mPackageId.readBytes(reader);
|
||||
mPackageName.readBytes(reader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append("LIBRARY{");
|
||||
builder.append(String.format("0x%02x", getPackageId()));
|
||||
builder.append(':');
|
||||
String name=getPackageName();
|
||||
if(name==null){
|
||||
name="NULL";
|
||||
}
|
||||
builder.append(name);
|
||||
builder.append('}');
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
607
src/main/java/com/reandroid/lib/arsc/value/ResConfig.java
Normal file
607
src/main/java/com/reandroid/lib/arsc/value/ResConfig.java
Normal file
@ -0,0 +1,607 @@
|
||||
package com.reandroid.lib.arsc.value;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockArray;
|
||||
import com.reandroid.lib.arsc.io.BlockLoad;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.ByteArray;
|
||||
import com.reandroid.lib.arsc.item.ByteItem;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.ShortItem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ResConfig extends BlockArray<Block> implements BlockLoad {
|
||||
private final IntegerItem configSize;
|
||||
private final ShortItem mcc;
|
||||
private final ShortItem mnc;
|
||||
private final ByteItem languageIn0;
|
||||
private final ByteItem languageIn1;
|
||||
private final ByteItem countryIn0;
|
||||
private final ByteItem countryIn1;
|
||||
private final ByteItem orientation;
|
||||
private final ByteItem touchscreen;
|
||||
private final ShortItem density;
|
||||
private final ByteItem keyboard;
|
||||
private final ByteItem navigation;
|
||||
private final ByteItem inputFlags;
|
||||
private final ByteItem inputPad0;
|
||||
private final ShortItem screenWidth;
|
||||
private final ShortItem screenHeight;
|
||||
private final ShortItem sdkVersion;
|
||||
private final ShortItem minorVersion;
|
||||
private final ByteItem screenLayout;
|
||||
private final ByteItem uiMode;
|
||||
private final ShortItem smallestScreenWidthDp;
|
||||
private final ShortItem screenWidthDp;
|
||||
private final ShortItem screenHeightDp;
|
||||
private final ByteArray localeScript;
|
||||
private final ByteArray localeVariant;
|
||||
private final ByteItem screenLayout2;
|
||||
private final ByteItem colorMode;
|
||||
private final ShortItem reservedPadding;
|
||||
private final ByteArray skipSizeGreater56;
|
||||
private final ByteArray exceedingSize;
|
||||
private final ByteArray remainingSize;
|
||||
private int mCurrentSize;
|
||||
private boolean mIsUpdatingSize;
|
||||
|
||||
private String mQualifiers;
|
||||
|
||||
|
||||
public ResConfig(){
|
||||
super();
|
||||
|
||||
this.configSize = new IntegerItem(64);
|
||||
this.mcc = new ShortItem();
|
||||
this.mnc = new ShortItem();
|
||||
this.languageIn0 = new ByteItem();
|
||||
this.languageIn1 = new ByteItem();
|
||||
this.countryIn0 = new ByteItem();
|
||||
this.countryIn1 = new ByteItem();
|
||||
this.orientation = new ByteItem();
|
||||
this.touchscreen = new ByteItem();
|
||||
this.density = new ShortItem();
|
||||
this.keyboard = new ByteItem();
|
||||
this.navigation = new ByteItem();
|
||||
this.inputFlags = new ByteItem();
|
||||
this.inputPad0 = new ByteItem();
|
||||
this.screenWidth = new ShortItem();
|
||||
this.screenHeight = new ShortItem();
|
||||
this.sdkVersion = new ShortItem();
|
||||
this.minorVersion = new ShortItem();
|
||||
this.screenLayout = new ByteItem();
|
||||
this.uiMode = new ByteItem();
|
||||
this.smallestScreenWidthDp = new ShortItem();
|
||||
this.screenWidthDp = new ShortItem();
|
||||
this.screenHeightDp = new ShortItem();
|
||||
// size sum=44;
|
||||
this.localeScript = new ByteArray(4);
|
||||
this.localeVariant = new ByteArray(8);
|
||||
this.screenLayout2 = new ByteItem();
|
||||
this.colorMode = new ByteItem();
|
||||
this.reservedPadding = new ShortItem();
|
||||
this.skipSizeGreater56 = new ByteArray(4);
|
||||
this.exceedingSize = new ByteArray(8);
|
||||
this.remainingSize = new ByteArray();
|
||||
|
||||
configSize.setBlockLoad(this);
|
||||
initChildes();
|
||||
mCurrentSize=64;
|
||||
setConfigSize(mCurrentSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRefreshed() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block newInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block[] newInstance(int len) {
|
||||
return new Block[len];
|
||||
}
|
||||
|
||||
public void parseName(String name){
|
||||
ResConfigHelper.parseQualifiers(this, name);
|
||||
mQualifiers=null;
|
||||
}
|
||||
|
||||
|
||||
private void initChildes(){
|
||||
add(configSize);
|
||||
add(mcc);
|
||||
add(mnc);
|
||||
add(languageIn0);
|
||||
add(languageIn1);
|
||||
add(countryIn0);
|
||||
add(countryIn1);
|
||||
add(orientation);
|
||||
add(touchscreen);
|
||||
add(density);
|
||||
add(keyboard);
|
||||
add(navigation);
|
||||
add(inputFlags);
|
||||
add(inputPad0);
|
||||
add(screenWidth);
|
||||
add(screenHeight);
|
||||
add(sdkVersion);
|
||||
add(minorVersion);
|
||||
add(screenLayout);
|
||||
add(uiMode);
|
||||
add(smallestScreenWidthDp);
|
||||
add(screenWidthDp);
|
||||
add(screenHeightDp);
|
||||
add(localeScript);
|
||||
add(localeVariant);
|
||||
add(screenLayout2);
|
||||
add(colorMode);
|
||||
add(reservedPadding);
|
||||
add(skipSizeGreater56);
|
||||
add(exceedingSize);
|
||||
add(remainingSize);
|
||||
}
|
||||
private void resetToDefault(){
|
||||
configSize.set(0);
|
||||
mcc.set((short) 0);
|
||||
mnc.set((short) 0);
|
||||
languageIn0.set((byte)0);
|
||||
languageIn1.set((byte)0);
|
||||
countryIn0.set((byte)0);
|
||||
countryIn1.set((byte)0);
|
||||
orientation.set((byte)0);
|
||||
touchscreen.set((byte)0);
|
||||
density.set((short) 0);
|
||||
keyboard.set((byte)0);
|
||||
navigation.set((byte)0);
|
||||
inputFlags.set((byte)0);
|
||||
inputPad0.set((byte)0);
|
||||
screenWidth.set((short) 0);
|
||||
screenHeight.set((short) 0);
|
||||
sdkVersion.set((short) 0);
|
||||
minorVersion.set((short) 0);
|
||||
screenLayout.set((byte)0);
|
||||
uiMode.set((byte)0);
|
||||
smallestScreenWidthDp.set((byte)0);
|
||||
screenWidthDp.set((byte)0);
|
||||
screenHeightDp.set((byte)0);
|
||||
localeScript.clear();
|
||||
localeVariant.clear();
|
||||
screenLayout2.set((byte)0);
|
||||
colorMode.set((byte)0);
|
||||
reservedPadding.set((short) 0);
|
||||
skipSizeGreater56.clear();
|
||||
exceedingSize.clear();
|
||||
remainingSize.clear();
|
||||
mCurrentSize=0;
|
||||
setConfigSize(DEFAULT_CONFIG_SIZE);
|
||||
}
|
||||
public void setConfigSize(int i){
|
||||
this.configSize.set(i);
|
||||
updateConfigSize(i);
|
||||
}
|
||||
public int getConfigSize(){
|
||||
return this.configSize.get();
|
||||
}
|
||||
public void setMcc(short sh){
|
||||
this.mcc.set(sh);
|
||||
}
|
||||
public short getMcc(){
|
||||
return this.mcc.get();
|
||||
}
|
||||
public void setMnc(short sh){
|
||||
this.mnc.set(sh);
|
||||
}
|
||||
public short getMnc(){
|
||||
return this.mnc.get();
|
||||
}
|
||||
public void setLanguageIn0(byte b){
|
||||
byte old=languageIn0.get();
|
||||
this.languageIn0.set(b);
|
||||
valuesChanged(b, old);
|
||||
}
|
||||
public byte getLanguageIn0(){
|
||||
return this.languageIn0.get();
|
||||
}
|
||||
public void setLanguageIn1(byte b){
|
||||
byte old=languageIn1.get();
|
||||
this.languageIn1.set(b);
|
||||
valuesChanged(b, old);
|
||||
}
|
||||
public byte getLanguageIn1(){
|
||||
return this.languageIn1.get();
|
||||
}
|
||||
public char[] getLanguage(){
|
||||
byte b0=getLanguageIn0();
|
||||
byte b1=getLanguageIn1();
|
||||
return unpackLanguageOrRegion(b0, b1, 'a');
|
||||
}
|
||||
public void setLanguage(char[] chs){
|
||||
byte[] bts;
|
||||
if(isNull(chs)){
|
||||
bts=new byte[2];
|
||||
}else {
|
||||
bts=packLanguageOrRegion(chs);
|
||||
}
|
||||
setLanguageIn0(bts[0]);
|
||||
setLanguageIn1(bts[1]);
|
||||
}
|
||||
public void setCountryIn0(byte b){
|
||||
byte old=countryIn0.get();
|
||||
this.countryIn0.set(b);
|
||||
valuesChanged(b, old);
|
||||
}
|
||||
public byte getCountryIn0(){
|
||||
return this.countryIn0.get();
|
||||
}
|
||||
public void setCountryIn1(byte b){
|
||||
byte old=countryIn1.get();
|
||||
this.countryIn1.set(b);
|
||||
valuesChanged(b, old);
|
||||
}
|
||||
public byte getCountryIn1(){
|
||||
return this.countryIn1.get();
|
||||
}
|
||||
public char[] getRegion(){
|
||||
byte b0=getCountryIn0();
|
||||
byte b1=getCountryIn1();
|
||||
return unpackLanguageOrRegion(b0, b1, '0');
|
||||
}
|
||||
public void setRegion(char[] chs){
|
||||
byte[] bts;
|
||||
if(isNull(chs)){
|
||||
bts=new byte[2];
|
||||
}else {
|
||||
bts=packLanguageOrRegion(chs);
|
||||
}
|
||||
setCountryIn0(bts[0]);
|
||||
setCountryIn1(bts[1]);
|
||||
}
|
||||
public void setOrientation(byte b){
|
||||
byte old=orientation.get();
|
||||
this.orientation.set(b);
|
||||
valuesChanged(b, old);
|
||||
}
|
||||
public byte getOrientation(){
|
||||
return this.orientation.get();
|
||||
}
|
||||
public void setTouchscreen(byte b){
|
||||
byte old=touchscreen.get();
|
||||
this.touchscreen.set(b);
|
||||
valuesChanged(b, old);
|
||||
}
|
||||
public byte getTouchscreen(){
|
||||
return this.touchscreen.get();
|
||||
}
|
||||
public void setDensity(short sh){
|
||||
short old=density.get();
|
||||
this.density.set(sh);
|
||||
valuesChanged(sh, old);
|
||||
}
|
||||
public short getDensity(){
|
||||
return this.density.get();
|
||||
}
|
||||
public void setKeyboard(byte b){
|
||||
this.keyboard.set(b);
|
||||
}
|
||||
public byte getKeyboard(){
|
||||
return this.keyboard.get();
|
||||
}
|
||||
public void setNavigation(byte b){
|
||||
this.navigation.set(b);
|
||||
}
|
||||
public byte getNavigation(){
|
||||
return this.navigation.get();
|
||||
}
|
||||
public void setInputFlags(byte b){
|
||||
this.inputFlags.set(b);
|
||||
}
|
||||
public byte getInputFlags(){
|
||||
return this.inputFlags.get();
|
||||
}
|
||||
public void setInputPad0(byte b){
|
||||
this.inputPad0.set(b);
|
||||
}
|
||||
public byte getInputPad0(){
|
||||
return this.inputPad0.get();
|
||||
}
|
||||
public void setScreenSize(short w, short h){
|
||||
this.setScreenWidth(w);
|
||||
this.setScreenHeight(h);
|
||||
}
|
||||
public void setScreenWidth(short sh){
|
||||
short old=screenWidth.get();
|
||||
this.screenWidth.set(sh);
|
||||
valuesChanged(sh, old);
|
||||
}
|
||||
public short getScreenWidth(){
|
||||
return this.screenWidth.get();
|
||||
}
|
||||
public void setScreenHeight(short sh){
|
||||
short old=screenHeight.get();
|
||||
this.screenHeight.set(sh);
|
||||
valuesChanged(sh, old);
|
||||
}
|
||||
public short getScreenHeight(){
|
||||
return this.screenHeight.get();
|
||||
}
|
||||
public void setSdkVersion(short sh){
|
||||
short old=sdkVersion.get();
|
||||
this.sdkVersion.set(sh);
|
||||
valuesChanged(sh, old);
|
||||
}
|
||||
public short getSdkVersion(){
|
||||
return this.sdkVersion.get();
|
||||
}
|
||||
public void setMinorVersion(short sh){
|
||||
this.minorVersion.set(sh);
|
||||
}
|
||||
public short getMinorVersion(){
|
||||
return this.minorVersion.get();
|
||||
}
|
||||
public void setScreenLayout(byte b){
|
||||
this.screenLayout.set(b);
|
||||
}
|
||||
public byte getScreenLayout(){
|
||||
return this.screenLayout.get();
|
||||
}
|
||||
public void setUiMode(byte b){
|
||||
this.uiMode.set(b);
|
||||
}
|
||||
public byte getUiMode(){
|
||||
return this.uiMode.get();
|
||||
}
|
||||
public void setSmallestScreenWidthDp(short sh){
|
||||
this.smallestScreenWidthDp.set(sh);
|
||||
}
|
||||
public short getSmallestScreenWidthDp(){
|
||||
return this.smallestScreenWidthDp.get();
|
||||
}
|
||||
public void setScreenWidthDp(short sh){
|
||||
this.screenWidthDp.set(sh);
|
||||
}
|
||||
public short getScreenWidthDp(){
|
||||
return this.screenWidthDp.get();
|
||||
}
|
||||
public void setScreenHeightDp(short sh){
|
||||
this.screenHeightDp.set(sh);
|
||||
}
|
||||
public short getScreenHeightDp(){
|
||||
return this.screenHeightDp.get();
|
||||
}
|
||||
public void setLocaleScript(byte[] bts){
|
||||
this.localeScript.set(bts);
|
||||
}
|
||||
public void setLocaleScript(char[] chs){
|
||||
byte[] bts=toByteArray(chs, localeScript.size());
|
||||
this.localeScript.set(bts);
|
||||
}
|
||||
public char[] getLocaleScript(){
|
||||
byte[] bts=localeScript.toArray();
|
||||
return toCharArray(bts);
|
||||
}
|
||||
public void setLocaleVariant(byte[] bts){
|
||||
this.localeVariant.set(bts);
|
||||
}
|
||||
public void setLocaleVariant(char[] chs){
|
||||
byte[] bts=toByteArray(chs, localeVariant.size());
|
||||
this.localeVariant.set(bts);
|
||||
}
|
||||
public char[] getLocaleVariant(){
|
||||
return toCharArray(localeVariant.toArray());
|
||||
}
|
||||
public void setScreenLayout2(byte b){
|
||||
this.screenLayout2.set(b);
|
||||
}
|
||||
public byte getScreenLayout2(){
|
||||
return this.screenLayout2.get();
|
||||
}
|
||||
public void setColorMode(byte b){
|
||||
this.colorMode.set(b);
|
||||
}
|
||||
public byte getColorMode(){
|
||||
return this.colorMode.get();
|
||||
}
|
||||
public void setReservedPadding(short sh){
|
||||
this.reservedPadding.set(sh);
|
||||
}
|
||||
public short getReservedPadding(){
|
||||
return this.reservedPadding.get();
|
||||
}
|
||||
|
||||
private void valuesChanged(int val, int old){
|
||||
if(val==old){
|
||||
return;
|
||||
}
|
||||
valuesChanged();
|
||||
}
|
||||
private void valuesChanged(){
|
||||
mQualifiers=null;
|
||||
}
|
||||
|
||||
public String getQualifiers(){
|
||||
if(mQualifiers==null){
|
||||
mQualifiers= ResConfigHelper.toQualifier(this);
|
||||
}
|
||||
return mQualifiers;
|
||||
}
|
||||
@Override
|
||||
public int countBytes(){
|
||||
if(mIsUpdatingSize){
|
||||
return super.countBytes();
|
||||
}
|
||||
return mCurrentSize;
|
||||
}
|
||||
|
||||
private void updateConfigSize(int sz){
|
||||
if(sz==mCurrentSize){
|
||||
return;
|
||||
}
|
||||
mIsUpdatingSize=true;
|
||||
mCurrentSize=sz;
|
||||
localeScript.setSize(4);
|
||||
if(sz<=48){
|
||||
localeVariant.setSize(4);
|
||||
exceedingSize.setSize(0);
|
||||
remainingSize.setSize(0);
|
||||
skipSizeGreater56.setSize(0);
|
||||
mIsUpdatingSize=false;
|
||||
return;
|
||||
}
|
||||
if(sz==KNOWN_CONFIG_BYTES){
|
||||
localeVariant.setSize(8);
|
||||
skipSizeGreater56.setSize(4);
|
||||
exceedingSize.setSize(0);
|
||||
remainingSize.setSize(0);
|
||||
mIsUpdatingSize=false;
|
||||
return;
|
||||
}
|
||||
if(sz<KNOWN_CONFIG_BYTES){
|
||||
int i=sz-KNOWN_CONFIG_BYTES;
|
||||
localeVariant.setSize(i);
|
||||
skipSizeGreater56.setSize(0);
|
||||
exceedingSize.setSize(0);
|
||||
remainingSize.setSize(0);
|
||||
mIsUpdatingSize=false;
|
||||
return;
|
||||
}
|
||||
int ex=sz-KNOWN_CONFIG_BYTES;
|
||||
localeVariant.setSize(8);
|
||||
skipSizeGreater56.setSize(4);
|
||||
exceedingSize.setSize(ex);
|
||||
int rem=sz-64;
|
||||
remainingSize.setSize(rem);
|
||||
mIsUpdatingSize=false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockLoaded(BlockReader reader, Block sender) throws IOException {
|
||||
if(sender==configSize){
|
||||
if(configSize.get()!=64){
|
||||
configSize.get();
|
||||
}
|
||||
setConfigSize(configSize.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
String q=getQualifiers();
|
||||
if(q.trim().length()==0){
|
||||
return "[DEFAULT]";
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
private static char[] unpackLanguageOrRegion(byte b0, byte b1, char base){
|
||||
if ((((b0 >> 7) & 1) == 1)) {
|
||||
int first = b1 & 0x1F;
|
||||
int x=((b1 & 0xE0) >> 5);
|
||||
int y=((b0 & 0x03) << 3);
|
||||
int second = x + y;
|
||||
int third = (b0 & 0x7C) >> 2;
|
||||
|
||||
return new char[] { (char) (first + base), (char) (second + base), (char) (third + base) };
|
||||
}
|
||||
return new char[] { (char) b0, (char) b1 };
|
||||
}
|
||||
private static byte[] packLanguageOrRegion(char[] chs){
|
||||
if(chs==null || chs.length<2){
|
||||
return new byte[2];
|
||||
}
|
||||
if(chs.length==2 || chs.length>3){
|
||||
byte[] result=new byte[2];
|
||||
result[0]=(byte) chs[0];
|
||||
result[1]=(byte) chs[1];
|
||||
return result;
|
||||
}
|
||||
int base=getBase(chs[0]);
|
||||
int first=chs[0] - base;
|
||||
int second=chs[1] - base;
|
||||
int third=chs[2] - base;
|
||||
|
||||
int b1Right=first & 0x1F;
|
||||
|
||||
int b0Left=third;
|
||||
b0Left=b0Left << 2;
|
||||
|
||||
int b1Left=second & 7;
|
||||
b1Left=b1Left<<5;
|
||||
|
||||
int b1=(b1Left | b1Right);
|
||||
|
||||
int b0Right=second >> 3;
|
||||
b0Right=b0Right & 0x3;
|
||||
|
||||
int b0=(b0Left | b0Right);
|
||||
return new byte[] { (byte) b0, (byte) b1 };
|
||||
}
|
||||
private static int getBase(char ch){
|
||||
int result=ch;
|
||||
if(ch>='0' && ch<='9'){
|
||||
result='0';
|
||||
}else if(ch>='a' && ch<='z'){
|
||||
result='a';
|
||||
}else if(ch>='A' && ch<='Z'){
|
||||
result='A';
|
||||
}
|
||||
result=result+32;
|
||||
return result;
|
||||
}
|
||||
private static byte[] toByteArray(char[] chs, int len){
|
||||
byte[] bts=new byte[len];
|
||||
if(chs==null){
|
||||
return bts;
|
||||
}
|
||||
int sz=chs.length;
|
||||
for(int i=0; i<sz;i++){
|
||||
bts[i]= (byte) chs[i];
|
||||
}
|
||||
return bts;
|
||||
}
|
||||
private static char[] toCharArray(byte[] bts){
|
||||
if(isNull(bts)){
|
||||
return null;
|
||||
}
|
||||
int sz=bts.length;
|
||||
char[] chs=new char[sz];
|
||||
for(int i=0; i<sz;i++){
|
||||
chs[i]= (char) bts[i];
|
||||
}
|
||||
return chs;
|
||||
}
|
||||
private static boolean isNull(char[] chs){
|
||||
if(chs==null){
|
||||
return true;
|
||||
}
|
||||
for(int i=0;i<chs.length;i++){
|
||||
if(chs[i]!=0){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private static boolean isNull(byte[] bts){
|
||||
if(bts==null){
|
||||
return true;
|
||||
}
|
||||
for(int i=0;i<bts.length;i++){
|
||||
if(bts[i]!=0){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private static final int KNOWN_CONFIG_BYTES = 56;
|
||||
|
||||
private static final int DEFAULT_CONFIG_SIZE = 64;
|
||||
|
||||
|
||||
}
|
1094
src/main/java/com/reandroid/lib/arsc/value/ResConfigHelper.java
Normal file
1094
src/main/java/com/reandroid/lib/arsc/value/ResConfigHelper.java
Normal file
File diff suppressed because it is too large
Load Diff
165
src/main/java/com/reandroid/lib/arsc/value/ResValueBag.java
Normal file
165
src/main/java/com/reandroid/lib/arsc/value/ResValueBag.java
Normal file
@ -0,0 +1,165 @@
|
||||
package com.reandroid.lib.arsc.value;
|
||||
|
||||
import com.reandroid.lib.arsc.array.ResValueBagItemArray;
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.base.BlockCounter;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
public class ResValueBag extends BaseResValue {
|
||||
|
||||
private final IntegerItem mParentId;
|
||||
private final IntegerItem mCount;
|
||||
private final ResValueBagItemArray mResValueBagItemArray;
|
||||
public ResValueBag(){
|
||||
super(0);
|
||||
mParentId =new IntegerItem();
|
||||
mCount=new IntegerItem();
|
||||
mResValueBagItemArray=new ResValueBagItemArray();
|
||||
|
||||
mParentId.setParent(this);
|
||||
mParentId.setIndex(0);
|
||||
mCount.setParent(this);
|
||||
mCount.setIndex(1);
|
||||
mResValueBagItemArray.setParent(this);
|
||||
mResValueBagItemArray.setIndex(2);
|
||||
}
|
||||
public ResValueBagItemArray getResValueBagItemArray(){
|
||||
return mResValueBagItemArray;
|
||||
}
|
||||
|
||||
public int getParentId(){
|
||||
return mParentId.get();
|
||||
}
|
||||
public void setParentId(int id){
|
||||
mParentId.set(id);
|
||||
}
|
||||
public int getCount(){
|
||||
return mCount.get();
|
||||
}
|
||||
public void setCount(int count){
|
||||
if(count<0){
|
||||
count=0;
|
||||
}
|
||||
mCount.set(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() {
|
||||
byte[] results = mParentId.getBytes();
|
||||
results=addBytes(results, mCount.getBytes());
|
||||
results=addBytes(results, mResValueBagItemArray.getBytes());
|
||||
return results;
|
||||
}
|
||||
@Override
|
||||
public int countBytes() {
|
||||
int result;
|
||||
result = mParentId.countBytes();
|
||||
result+=mCount.countBytes();
|
||||
result+=mResValueBagItemArray.countBytes();
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public void onCountUpTo(BlockCounter counter) {
|
||||
if(countSubChildes(counter, mParentId)){
|
||||
return;
|
||||
}
|
||||
if(countSubChildes(counter, mCount)){
|
||||
return;
|
||||
}
|
||||
countSubChildes(counter, mResValueBagItemArray);
|
||||
}
|
||||
private boolean countSubChildes(BlockCounter counter, Block child){
|
||||
if(counter.FOUND){
|
||||
return true;
|
||||
}
|
||||
if(counter.END==child){
|
||||
counter.FOUND=true;
|
||||
return true;
|
||||
}
|
||||
int c=child.countBytes();
|
||||
counter.addCount(c);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void refreshCount(){
|
||||
setCount(getResValueBagItemArray().childesCount());
|
||||
}
|
||||
@Override
|
||||
protected int onWriteBytes(OutputStream writer) throws IOException {
|
||||
if(isNull()){
|
||||
return 0;
|
||||
}
|
||||
refreshCount();
|
||||
int result;
|
||||
result=mParentId.writeBytes(writer);
|
||||
result+=mCount.writeBytes(writer);
|
||||
result+=mResValueBagItemArray.writeBytes(writer);
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException{
|
||||
mResValueBagItemArray.clearChildes();
|
||||
mParentId.readBytes(reader);
|
||||
mCount.readBytes(reader);
|
||||
mResValueBagItemArray.setChildesCount(mCount.get());
|
||||
mResValueBagItemArray.readBytes(reader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(getClass().getSimpleName());
|
||||
builder.append(": parent=");
|
||||
builder.append(String.format("0x%08x", getParentId()));
|
||||
builder.append(", count=");
|
||||
builder.append(getCount());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
||||
// NOT Applicable
|
||||
@Override
|
||||
void setHeaderSize(short size) {
|
||||
}
|
||||
// NOT Applicable
|
||||
@Override
|
||||
short getHeaderSize() {
|
||||
return HEADER_COMPLEX;
|
||||
}
|
||||
// NOT Applicable
|
||||
@Override
|
||||
void setReserved(byte reserved) {
|
||||
}
|
||||
// NOT Applicable
|
||||
@Override
|
||||
byte getReserved() {
|
||||
return 0;
|
||||
}
|
||||
// NOT Applicable
|
||||
@Override
|
||||
public void setType(byte type) {
|
||||
}
|
||||
|
||||
// NOT Applicable
|
||||
@Override
|
||||
public byte getType() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOT Applicable
|
||||
@Override
|
||||
public int getData() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOT Applicable
|
||||
@Override
|
||||
public void setData(int data) { }
|
||||
|
||||
private final static short HEADER_COMPLEX=0x0010;
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package com.reandroid.lib.arsc.value;
|
||||
|
||||
public class ResValueBagItem extends BaseResValue {
|
||||
|
||||
public ResValueBagItem() {
|
||||
super(BYTES_COUNT);
|
||||
setHeaderSize(BYTES_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
void setHeaderSize(short size) {
|
||||
setShort(OFFSET_SIZE, size);
|
||||
}
|
||||
@Override
|
||||
short getHeaderSize() {
|
||||
return getShort(OFFSET_SIZE);
|
||||
}
|
||||
@Override
|
||||
void setReserved(byte reserved) {
|
||||
setByte(OFFSET_RESERVED, reserved);
|
||||
}
|
||||
@Override
|
||||
byte getReserved() {
|
||||
return getByte(OFFSET_RESERVED);
|
||||
}
|
||||
|
||||
|
||||
public void setId(int id){
|
||||
setInt(OFFSET_ID, id);
|
||||
}
|
||||
public int getId(){
|
||||
return getInt(OFFSET_ID);
|
||||
}
|
||||
@Override
|
||||
public void setType(byte type){
|
||||
setByte(OFFSET_TYPE, type);
|
||||
}
|
||||
@Override
|
||||
public byte getType(){
|
||||
return getByte(OFFSET_TYPE);
|
||||
}
|
||||
@Override
|
||||
public int getData(){
|
||||
return getInt(OFFSET_DATA);
|
||||
}
|
||||
@Override
|
||||
public void setData(int data){
|
||||
setInt(OFFSET_DATA, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(getClass().getSimpleName());
|
||||
builder.append(" type=");
|
||||
ValueType vt=getValueType();
|
||||
if(vt!=null){
|
||||
builder.append(vt.name());
|
||||
}else {
|
||||
builder.append("Unknown");
|
||||
}
|
||||
builder.append('(');
|
||||
builder.append(String.format("0x%02x", getType()));
|
||||
builder.append(") id=");
|
||||
builder.append(String.format("0x%08x", getId()));
|
||||
builder.append(", data=");
|
||||
builder.append(String.format("0x%08x", getData()));
|
||||
return builder.toString();
|
||||
}
|
||||
private static final int OFFSET_ID=0;
|
||||
private static final int OFFSET_SIZE=4;
|
||||
private static final int OFFSET_RESERVED=6;
|
||||
private static final int OFFSET_TYPE=7;
|
||||
private static final int OFFSET_DATA=8;
|
||||
|
||||
private static final int BYTES_COUNT=12;
|
||||
|
||||
private static final short BYTES_SIZE=0x08;
|
||||
|
||||
}
|
77
src/main/java/com/reandroid/lib/arsc/value/ResValueInt.java
Normal file
77
src/main/java/com/reandroid/lib/arsc/value/ResValueInt.java
Normal file
@ -0,0 +1,77 @@
|
||||
package com.reandroid.lib.arsc.value;
|
||||
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ResValueInt extends BaseResValue {
|
||||
public ResValueInt() {
|
||||
super(BYTES_COUNT);
|
||||
setHeaderSize(BYTES_SIZE);
|
||||
}
|
||||
@Override
|
||||
void setHeaderSize(short size) {
|
||||
setShort(OFFSET_SIZE, size);
|
||||
}
|
||||
@Override
|
||||
short getHeaderSize() {
|
||||
return getShort(OFFSET_SIZE);
|
||||
}
|
||||
@Override
|
||||
void setReserved(byte reserved) {
|
||||
setByte(OFFSET_RESERVED, reserved);
|
||||
}
|
||||
@Override
|
||||
byte getReserved() {
|
||||
return getByte(OFFSET_RESERVED);
|
||||
}
|
||||
@Override
|
||||
public void setType(byte type){
|
||||
setByte(OFFSET_TYPE, type);
|
||||
}
|
||||
@Override
|
||||
public byte getType(){
|
||||
return getByte(OFFSET_TYPE);
|
||||
}
|
||||
@Override
|
||||
public int getData(){
|
||||
return getInt(OFFSET_DATA);
|
||||
}
|
||||
@Override
|
||||
public void setData(int data){
|
||||
setInt(OFFSET_DATA, data);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
super.onReadBytes(reader);
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(getClass().getSimpleName());
|
||||
builder.append(" type=");
|
||||
ValueType vt=getValueType();
|
||||
if(vt!=null){
|
||||
builder.append(vt.name());
|
||||
}else {
|
||||
builder.append("Unknown");
|
||||
}
|
||||
builder.append('(');
|
||||
builder.append(String.format("0x%02x", getType()));
|
||||
builder.append("), data=");
|
||||
builder.append(String.format("0x%08x", getData()));
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static final int OFFSET_SIZE=0;
|
||||
private static final int OFFSET_RESERVED=2;
|
||||
private static final int OFFSET_TYPE=3;
|
||||
private static final int OFFSET_DATA=4;
|
||||
|
||||
private static final int BYTES_COUNT=8;
|
||||
|
||||
private static final short BYTES_SIZE=0x08;
|
||||
}
|
56
src/main/java/com/reandroid/lib/arsc/value/ValueType.java
Normal file
56
src/main/java/com/reandroid/lib/arsc/value/ValueType.java
Normal file
@ -0,0 +1,56 @@
|
||||
package com.reandroid.lib.arsc.value;
|
||||
|
||||
|
||||
public enum ValueType {
|
||||
|
||||
NULL((byte) 0x00),
|
||||
REFERENCE((byte) 0x01),
|
||||
ATTRIBUTE((byte) 0x02),
|
||||
STRING((byte) 0x03),
|
||||
FLOAT((byte) 0x04),
|
||||
DIMENSION((byte) 0x05),
|
||||
FRACTION((byte) 0x06),
|
||||
DYNAMIC_REFERENCE((byte) 0x07),
|
||||
DYNAMIC_ATTRIBUTE((byte) 0x08),
|
||||
FIRST_INT((byte) 0x10),
|
||||
INT_DEC((byte) 0x10),
|
||||
INT_HEX((byte) 0x11),
|
||||
INT_BOOLEAN((byte) 0x12),
|
||||
FIRST_COLOR_INT((byte) 0x1c),
|
||||
INT_COLOR_ARGB8((byte) 0x1c),
|
||||
INT_COLOR_RGB8((byte) 0x1d),
|
||||
INT_COLOR_ARGB4((byte) 0x1e),
|
||||
INT_COLOR_RGB4((byte) 0x1f),
|
||||
LAST_COLOR_INT((byte) 0x1f),
|
||||
LAST_INT((byte) 0x1f);
|
||||
|
||||
private final byte mByte;
|
||||
ValueType(byte b) {
|
||||
this.mByte=b;
|
||||
}
|
||||
public byte getByte(){
|
||||
return mByte;
|
||||
}
|
||||
public static ValueType valueOf(byte b){
|
||||
ValueType[] all=values();
|
||||
for(ValueType vt:all){
|
||||
if(vt.mByte==b){
|
||||
return vt;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static ValueType fromName(String name){
|
||||
if(name==null){
|
||||
return null;
|
||||
}
|
||||
name=name.toUpperCase();
|
||||
ValueType[] all=values();
|
||||
for(ValueType vt:all){
|
||||
if(name.equals(vt.name())){
|
||||
return vt;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
4
src/main/resources/lib.properties
Normal file
4
src/main/resources/lib.properties
Normal file
@ -0,0 +1,4 @@
|
||||
lib.name=ARSCLib
|
||||
lib.version=${version}
|
||||
lib.repo=https://github.com/REAndroid/ARSCLib
|
||||
lib.description=Android binary resources read/write library
|
Loading…
x
Reference in New Issue
Block a user