diff --git a/src/main/java/com/reandroid/arsc/array/SparseTypeBlockArray.java b/src/main/java/com/reandroid/arsc/array/SparseTypeBlockArray.java new file mode 100644 index 0000000..213dd3f --- /dev/null +++ b/src/main/java/com/reandroid/arsc/array/SparseTypeBlockArray.java @@ -0,0 +1,39 @@ + /* + * Copyright (C) 2022 github.com/REAndroid + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.reandroid.arsc.array; + +import com.reandroid.arsc.base.BlockArray; +import com.reandroid.arsc.chunk.SparseTypeBlock; + +public class SparseTypeBlockArray extends BlockArray { + @Override + public SparseTypeBlock[] newInstance(int len) { + return new SparseTypeBlock[len]; + } + @Override + public SparseTypeBlock newInstance() { + return new SparseTypeBlock(); + } + @Override + protected void onRefreshed() { + } + public void merge(SparseTypeBlockArray sparse){ + if(sparse == null || sparse == this){ + return; + } + addAll(sparse.getChildes()); + } +} diff --git a/src/main/java/com/reandroid/arsc/array/SparseTypeEntryArray.java b/src/main/java/com/reandroid/arsc/array/SparseTypeEntryArray.java new file mode 100644 index 0000000..7a09b8e --- /dev/null +++ b/src/main/java/com/reandroid/arsc/array/SparseTypeEntryArray.java @@ -0,0 +1,42 @@ + /* + * Copyright (C) 2022 github.com/REAndroid + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.reandroid.arsc.array; + +import com.reandroid.arsc.base.BlockArray; +import com.reandroid.arsc.io.BlockReader; +import com.reandroid.arsc.value.SparseTypeEntry; + +import java.io.IOException; + +public class SparseTypeEntryArray extends BlockArray { + @Override + public SparseTypeEntry[] newInstance(int len) { + return new SparseTypeEntry[len]; + } + @Override + public SparseTypeEntry newInstance() { + return new SparseTypeEntry(); + } + @Override + protected void onRefreshed() { + } + @Override + public void onReadBytes(BlockReader reader) throws IOException { + int count = reader.available() / 4; + setChildesCount(count); + super.onReadBytes(reader); + } +} diff --git a/src/main/java/com/reandroid/arsc/chunk/SparseTypeBlock.java b/src/main/java/com/reandroid/arsc/chunk/SparseTypeBlock.java new file mode 100644 index 0000000..4bcf79c --- /dev/null +++ b/src/main/java/com/reandroid/arsc/chunk/SparseTypeBlock.java @@ -0,0 +1,42 @@ + /* + * Copyright (C) 2022 github.com/REAndroid + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.reandroid.arsc.chunk; + +import com.reandroid.arsc.array.SparseTypeEntryArray; +import com.reandroid.arsc.header.TypeHeader; +import com.reandroid.arsc.value.SparseTypeEntry; + +import java.util.Collection; + +public class SparseTypeBlock extends Chunk{ + private final SparseTypeEntryArray entryArray; + public SparseTypeBlock() { + super(new TypeHeader(), 1); + entryArray = new SparseTypeEntryArray(); + addChild(entryArray); + getHeaderBlock().setSparse(true); + } + public Collection listEntries(){ + return getEntryArray().listItems(); + } + public SparseTypeEntryArray getEntryArray() { + return entryArray; + } + @Override + protected void onChunkRefreshed() { + getHeaderBlock().setSparse(true); + } +} diff --git a/src/main/java/com/reandroid/arsc/container/PackageBody.java b/src/main/java/com/reandroid/arsc/container/PackageBody.java index 9d653ba..a6aba58 100755 --- a/src/main/java/com/reandroid/arsc/container/PackageBody.java +++ b/src/main/java/com/reandroid/arsc/container/PackageBody.java @@ -116,8 +116,14 @@ public class PackageBody extends FixedBlockContainer { private void readTypeBlock(BlockReader reader) throws IOException{ TypeHeader typeHeader = reader.readTypeHeader(); SpecTypePair specTypePair = mSpecTypePairArray.getOrCreate(typeHeader.getId().get()); - TypeBlock typeBlock = specTypePair.getTypeBlockArray().createNext(); - typeBlock.readBytes(reader); + if(typeHeader.isSparse()){ + SparseTypeBlock sparseTypeBlock = specTypePair + .getSparseTypeBlockArray().createNext(); + sparseTypeBlock.readBytes(reader); + }else { + TypeBlock typeBlock = specTypePair.getTypeBlockArray().createNext(); + typeBlock.readBytes(reader); + } } private void readLibraryBlock(BlockReader reader) throws IOException{ LibraryBlock libraryBlock=new LibraryBlock(); diff --git a/src/main/java/com/reandroid/arsc/container/SpecTypePair.java b/src/main/java/com/reandroid/arsc/container/SpecTypePair.java index 1d97aca..1eb1068 100755 --- a/src/main/java/com/reandroid/arsc/container/SpecTypePair.java +++ b/src/main/java/com/reandroid/arsc/container/SpecTypePair.java @@ -16,15 +16,14 @@ package com.reandroid.arsc.container; import com.reandroid.arsc.array.EntryArray; -import com.reandroid.arsc.chunk.ChunkType; +import com.reandroid.arsc.array.SparseTypeBlockArray; +import com.reandroid.arsc.chunk.*; import com.reandroid.arsc.array.TypeBlockArray; import com.reandroid.arsc.base.Block; import com.reandroid.arsc.base.BlockContainer; -import com.reandroid.arsc.chunk.PackageBlock; -import com.reandroid.arsc.chunk.SpecBlock; -import com.reandroid.arsc.chunk.TypeBlock; import com.reandroid.arsc.group.EntryGroup; import com.reandroid.arsc.header.HeaderBlock; +import com.reandroid.arsc.header.TypeHeader; import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.item.TypeString; import com.reandroid.arsc.value.Entry; @@ -40,18 +39,31 @@ public class SpecTypePair extends BlockContainer private final Block[] mChildes; private final SpecBlock mSpecBlock; private final TypeBlockArray mTypeBlockArray; + private final SparseTypeBlockArray sparseTypeBlockArray; public SpecTypePair(SpecBlock specBlock, TypeBlockArray typeBlockArray){ this.mSpecBlock = specBlock; this.mTypeBlockArray = typeBlockArray; - this.mChildes=new Block[]{specBlock, typeBlockArray}; + this.sparseTypeBlockArray = new SparseTypeBlockArray(); + + this.mChildes=new Block[]{specBlock, typeBlockArray, sparseTypeBlockArray}; mSpecBlock.setIndex(0); mTypeBlockArray.setIndex(1); + sparseTypeBlockArray.setIndex(2); mSpecBlock.setParent(this); mTypeBlockArray.setParent(this); + sparseTypeBlockArray.setParent(this); } public SpecTypePair(){ this(new SpecBlock(), new TypeBlockArray()); } + + public SparseTypeBlockArray getSparseTypeBlockArray() { + return sparseTypeBlockArray; + } + public Collection listSparseTypeBlock(){ + return sparseTypeBlockArray.listItems(); + } + public Map createEntryGroups(){ Map map = new HashMap<>(); for(TypeBlock typeBlock:listTypeBlocks()){ @@ -211,7 +223,7 @@ public class SpecTypePair extends BlockContainer } ChunkType chunkType=headerBlock.getChunkType(); if(chunkType == ChunkType.TYPE){ - mTypeBlockArray.readBytes(reader); + readTypeBlock(reader); return; } if(chunkType!=ChunkType.SPEC){ @@ -219,6 +231,15 @@ public class SpecTypePair extends BlockContainer } mSpecBlock.readBytes(reader); } + private void readTypeBlock(BlockReader reader) throws IOException { + TypeHeader typeHeader = reader.readTypeHeader(); + if(typeHeader.isSparse()){ + SparseTypeBlock sparseTypeBlock = sparseTypeBlockArray.createNext(); + sparseTypeBlock.readBytes(reader); + return; + } + mTypeBlockArray.readBytes(reader); + } private void readUnexpectedNonSpecBlock(BlockReader reader, HeaderBlock headerBlock) throws IOException{ throw new IOException("Unexpected block: "+headerBlock.toString()+", Should be: "+ChunkType.SPEC); } @@ -260,6 +281,7 @@ public class SpecTypePair extends BlockContainer } getSpecBlock().merge(typePair.getSpecBlock()); getTypeBlockArray().merge(typePair.getTypeBlockArray()); + getSparseTypeBlockArray().merge(typePair.getSparseTypeBlockArray()); } @Override public int compareTo(SpecTypePair specTypePair) { diff --git a/src/main/java/com/reandroid/arsc/header/TypeHeader.java b/src/main/java/com/reandroid/arsc/header/TypeHeader.java index 2e3911b..675e0b1 100644 --- a/src/main/java/com/reandroid/arsc/header/TypeHeader.java +++ b/src/main/java/com/reandroid/arsc/header/TypeHeader.java @@ -43,6 +43,18 @@ public class TypeHeader extends HeaderBlock{ addChild(entriesStart); addChild(config); } + public boolean isSparse(){ + return (getFlags().get() & FLAG_SPARSE) == FLAG_SPARSE; + } + public void setSparse(boolean sparse){ + byte flag = getFlags().get(); + if(sparse){ + flag = (byte) (flag | FLAG_SPARSE); + }else { + flag = (byte) (flag & (~FLAG_SPARSE & 0xff)); + } + getFlags().set(flag); + } @Override public int getMinimumSize(){ @@ -77,6 +89,8 @@ public class TypeHeader extends HeaderBlock{ +", config=" + getConfig() + '}'; } + private static final byte FLAG_SPARSE = 0x1; + //typeHeader.countBytes() - getConfig().countBytes() + ResConfig.SIZE_16 private static final int TYPE_MIN_SIZE = 36; } diff --git a/src/main/java/com/reandroid/arsc/value/SparseTypeEntry.java b/src/main/java/com/reandroid/arsc/value/SparseTypeEntry.java new file mode 100644 index 0000000..0a3d893 --- /dev/null +++ b/src/main/java/com/reandroid/arsc/value/SparseTypeEntry.java @@ -0,0 +1,40 @@ + /* + * Copyright (C) 2022 github.com/REAndroid + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.reandroid.arsc.value; + +import com.reandroid.arsc.item.BlockItem; + +public class SparseTypeEntry extends BlockItem { + public SparseTypeEntry() { + super(4); + } + public int getIdx(){ + return getShort(getBytesInternal(), 0) & 0xffff; + } + public void setIdx(int idx){ + putShort(getBytesInternal(), 0, (short) idx); + } + public int getOffset(){ + return getShort(getBytesInternal(), 2) & 0xffff; + } + public void setOffset(int offset){ + putShort(getBytesInternal(), 2, (short) offset); + } + @Override + public String toString(){ + return "idx=" + getIdx() + ", offset=" + getOffset(); + } +}