Initial commit

This commit is contained in:
REAndroid 2021-11-14 22:53:01 +08:00
parent f3cce6a38e
commit ea16cbbb19
88 changed files with 7807 additions and 0 deletions

29
build.gradle Normal file
View 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

Binary file not shown.

View 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
View File

@ -0,0 +1,2 @@
rootProject.name = 'ARSCLib'

View 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;
}
}

View File

@ -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];
}
}

View File

@ -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);
}
}

View 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();
}
}

View 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());
}
}
}

View File

@ -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() {
}
}

View File

@ -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);
}
}

View File

@ -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() {
}
}

View File

@ -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;
}
}

View File

@ -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];
}
}

View File

@ -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() {
}
}

View File

@ -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];
}
}

View File

@ -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() {
}
}

View 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
}
}

View 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;
}

View File

@ -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];
}
}

View 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;
}
}

View File

@ -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];
}
}

View 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;
}
}

View 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();
}
}

View File

@ -0,0 +1,5 @@
package com.reandroid.lib.arsc.base;
public interface BlockArrayCreator<T extends Block> extends BlockCreator<T>{
T[] newInstance(int len);
}

View 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();
}

View 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;
}
}

View File

@ -0,0 +1,5 @@
package com.reandroid.lib.arsc.base;
public interface BlockCreator<T extends Block> {
T newInstance();
}

View 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();
}
}

View 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();
}
}

View 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
};
}

View 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());
}
}

View 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();
}
}

View 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();
}
}

View 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();
}
}

View 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();
}
}

View 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();
}
}

View File

@ -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();
}
}

View File

@ -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() {
}
}

View File

@ -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());
}
}
}

View File

@ -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;
}
}

View File

@ -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()+"\"";
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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()+"\"";
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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];
}
}

View File

@ -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";
}
}

View File

@ -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);
}
}

View 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();
}
}

View 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;
}

View 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;
}
}

View 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();
}
}

View 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() {
}
}

View 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());
}
}

View 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() {
}
}

View 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());
}
}

View 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;
}

View 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());
}
}

View File

@ -0,0 +1,7 @@
package com.reandroid.lib.arsc.item;
public class ResXmlString extends StringItem {
public ResXmlString(boolean utf8) {
super(utf8);
}
}

View 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());
}
}

View 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;
}
}

View 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();
}

View 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;
}

View File

@ -0,0 +1,7 @@
package com.reandroid.lib.arsc.item;
public class TableString extends StringItem {
public TableString(boolean utf8) {
super(utf8);
}
}

View 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;
}
}

View File

@ -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+")";
}
}

View 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;
}

View File

@ -0,0 +1,8 @@
package com.reandroid.lib.arsc.pool;
public enum PoolType {
TABLE,
SPEC,
TYPE,
XML
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View 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+"}";
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View 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];
}
}

View 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;
}

View 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();
}
}

View 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;
}

File diff suppressed because it is too large Load Diff

View 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;
}

View File

@ -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;
}

View 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;
}

View 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;
}
}

View 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