This commit is contained in:
REAndroid 2022-12-18 08:23:21 -05:00
parent 73dd9624cf
commit 41ed3dca11
28 changed files with 1028 additions and 91 deletions

View File

@ -2,7 +2,7 @@
apply plugin: 'java-library' apply plugin: 'java-library'
group 'com.reandroid.lib.arsc' group 'com.reandroid.lib.arsc'
version '1.0.7' version '1.0.8'
java { java {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
@ -22,7 +22,6 @@ repositories {
} }
dependencies { dependencies {
compile(files("$rootProject.projectDir/libs/ArchiveUtil.jar"))
} }
processResources { processResources {

Binary file not shown.

View File

@ -0,0 +1,76 @@
package com.reandroid.archive;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipFile;
public class APKArchive extends ZipArchive {
public APKArchive(Map<String, InputSource> entriesMap){
super(entriesMap);
}
public APKArchive(){
super();
}
public void sortApkFiles(){
sortApkFiles(new ArrayList<>(listInputSources()));
}
public long writeApk(File outApk) throws IOException{
ZipSerializer serializer=new ZipSerializer(listInputSources());
return serializer.writeZip(outApk);
}
public long writeApk(OutputStream outputStream) throws IOException{
ZipSerializer serializer=new ZipSerializer(listInputSources());
return serializer.writeZip(outputStream);
}
public static APKArchive loadZippedApk(File zipFile) throws IOException {
return loadZippedApk(new ZipFile(zipFile));
}
public static APKArchive loadZippedApk(ZipFile zipFile) {
Map<String, InputSource> entriesMap = InputSourceUtil.mapZipFileSources(zipFile);
return new APKArchive(entriesMap);
}
public static void repackApk(File apkFile) throws IOException{
APKArchive apkArchive =loadZippedApk(apkFile);
apkArchive.writeApk(apkFile);
}
public static void sortApkFiles(List<InputSource> sourceList){
Comparator<InputSource> cmp=new Comparator<InputSource>() {
@Override
public int compare(InputSource in1, InputSource in2) {
return getSortName(in1).compareTo(getSortName(in2));
}
};
sourceList.sort(cmp);
int i=0;
for(InputSource inputSource:sourceList){
inputSource.setSort(i);
i++;
}
}
private static String getSortName(InputSource inputSource){
String name=inputSource.getAlias();
StringBuilder builder=new StringBuilder();
if(name.equals("AndroidManifest.xml")){
builder.append("0 ");
}else if(name.equals("resources.arsc")){
builder.append("1 ");
}else if(name.startsWith("classes")){
builder.append("2 ");
}else if(name.startsWith("res/")){
builder.append("3 ");
}else if(name.startsWith("lib/")){
builder.append("4 ");
}else if(name.startsWith("assets/")){
builder.append("5 ");
}else {
builder.append("6 ");
}
builder.append(name.toLowerCase());
return builder.toString();
}
}

View File

@ -0,0 +1,27 @@
package com.reandroid.archive;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class ByteInputSource extends InputSource{
private final byte[] inBytes;
public ByteInputSource(byte[] inBytes, String name) {
super(name);
this.inBytes=inBytes;
}
@Override
public long write(OutputStream outputStream) throws IOException {
byte[] bts=getBytes();
outputStream.write(bts);
return bts.length;
}
@Override
public InputStream openStream() throws IOException {
return new ByteArrayInputStream(getBytes());
}
public byte[] getBytes() {
return inBytes;
}
}

View File

@ -0,0 +1,22 @@
package com.reandroid.archive;
import java.io.*;
public class FileInputSource extends InputSource{
private final File file;
public FileInputSource(File file, String name){
super(name);
this.file=file;
}
@Override
public void close(InputStream inputStream) throws IOException {
inputStream.close();
}
@Override
public FileInputStream openStream() throws IOException {
return new FileInputStream(this.file);
}
public File getFile(){
return file;
}
}

View File

@ -0,0 +1,108 @@
package com.reandroid.archive;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
public abstract class InputSource {
private final String name;
private String alias;
private long mCrc;
private long mLength;
private int method = ZipEntry.DEFLATED;
private int sort;
public InputSource(String name){
this.name = name;
this.alias = InputSourceUtil.sanitize(name);
}
public int getSort() {
return sort;
}
public void setSort(int sort) {
this.sort = sort;
}
public int getMethod() {
return method;
}
public void setMethod(int method) {
this.method = method;
}
public String getAlias(){
if(alias!=null){
return alias;
}
return getName();
}
public void setAlias(String alias) {
this.alias = alias;
}
public void close(InputStream inputStream) throws IOException {
inputStream.close();
}
public long write(OutputStream outputStream) throws IOException {
return write(outputStream, openStream());
}
private long write(OutputStream outputStream, InputStream inputStream) throws IOException {
long result=0;
byte[] buffer=new byte[10240];
int len;
while ((len=inputStream.read(buffer))>0){
outputStream.write(buffer, 0, len);
result+=len;
}
close(inputStream);
return result;
}
public String getName(){
return name;
}
public long getLength() throws IOException{
if(mLength==0){
calculateCrc();
}
return mLength;
}
public long getCrc() throws IOException{
if(mCrc==0){
calculateCrc();
}
return mCrc;
}
public abstract InputStream openStream() throws IOException;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof InputSource)) {
return false;
}
InputSource that = (InputSource) o;
return getName().equals(that.getName());
}
@Override
public int hashCode() {
return getName().hashCode();
}
@Override
public String toString(){
return getClass().getSimpleName()+": "+getName();
}
private void calculateCrc() throws IOException {
InputStream inputStream=openStream();
long length=0;
CRC32 crc = new CRC32();
int bytesRead;
byte[] buffer = new byte[1024*64];
while((bytesRead = inputStream.read(buffer)) != -1) {
crc.update(buffer, 0, bytesRead);
length+=bytesRead;
}
close(inputStream);
mCrc=crc.getValue();
mLength=length;
}
}

View File

@ -0,0 +1,107 @@
package com.reandroid.archive;
import java.io.File;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class InputSourceUtil {
public static String toRelative(File rootDir, File file){
int len=rootDir.getAbsolutePath().length();
String path=file.getAbsolutePath();
path=path.substring(len);
path=sanitize(path);
return path;
}
public static String sanitize(String path){
path=path.replace('\\', '/');
while (path.startsWith("./")){
path=path.substring(2);
}
while (path.startsWith("/")){
path=path.substring(1);
}
while (path.startsWith(File.separator)){
path=path.substring(1);
}
return path;
}
public static Map<String, InputSource> mapZipFileSources(ZipFile zipFile){
Map<String, InputSource> results=new HashMap<>();
Enumeration<? extends ZipEntry> entriesEnum = zipFile.entries();
int i=0;
while (entriesEnum.hasMoreElements()){
ZipEntry zipEntry = entriesEnum.nextElement();
if(zipEntry.isDirectory()){
continue;
}
ZipEntrySource source=new ZipEntrySource(zipFile, zipEntry);
source.setSort(i);
results.put(source.getName(), source);
i++;
}
return results;
}
public static List<InputSource> listZipFileSources(ZipFile zipFile){
List<InputSource> results=new ArrayList<>();
Enumeration<? extends ZipEntry> entriesEnum = zipFile.entries();
int i=0;
while (entriesEnum.hasMoreElements()){
ZipEntry zipEntry = entriesEnum.nextElement();
if(zipEntry.isDirectory()){
continue;
}
ZipEntrySource source=new ZipEntrySource(zipFile, zipEntry);
source.setSort(i);
results.add(source);
}
return results;
}
public static List<InputSource> listDirectory(File dir){
List<InputSource> results=new ArrayList<>();
recursiveDirectory(results, dir, dir);
return results;
}
private static void recursiveDirectory(List<InputSource> results, File rootDir, File dir){
if(dir.isFile()){
String name;
if(rootDir.equals(dir)){
name=dir.getName();
}else {
name=toRelative(rootDir, dir);
}
results.add(new FileInputSource(dir, name));
return;
}
File[] childFiles=dir.listFiles();
if(childFiles==null){
return;
}
for(File file:childFiles){
recursiveDirectory(results, rootDir, file);
}
}
public static List<String> sortString(List<String> stringList){
Comparator<String> cmp=new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
};
stringList.sort(cmp);
return stringList;
}
public static List<InputSource> sort(List<InputSource> sourceList){
Comparator<InputSource> cmp=new Comparator<InputSource>() {
@Override
public int compare(InputSource in1, InputSource in2) {
return Integer.compare(in1.getSort(), in2.getSort());
}
};
sourceList.sort(cmp);
return sourceList;
}
}

View File

@ -0,0 +1,5 @@
package com.reandroid.archive;
public interface WriteProgress {
void onCompressFile(String path, int mode, long writtenBytes);
}

View File

@ -0,0 +1,327 @@
/*
This class is copied from "apksigner" and I couldn't find the
original repo/author to credit here.
*/
package com.reandroid.archive;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class ZipAlign {
private static final int ZIP_ENTRY_HEADER_LEN = 30;
private static final int ZIP_ENTRY_VERSION = 20;
private static final int ZIP_ENTRY_USES_DATA_DESCR = 0x0008;
private static final int ZIP_ENTRY_DATA_DESCRIPTOR_LEN = 16;
private static final int ALIGNMENT_4 = 4;
private static class XEntry {
public final ZipEntry entry;
public final long headerOffset;
public final int flags;
public final int padding;
public XEntry(ZipEntry entry, long headerOffset, int flags, int padding) {
this.entry = entry;
this.headerOffset = headerOffset;
this.flags = flags;
this.padding = padding;
}
}
private static class FilterOutputStreamEx extends FilterOutputStream {
private long totalWritten = 0;
public FilterOutputStreamEx(OutputStream out) {
super(out);
}
@Override
public void write(byte[] b) throws IOException {
out.write(b);
totalWritten += b.length;
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
totalWritten += len;
}
@Override
public void write(int b) throws IOException {
out.write(b);
totalWritten += 1;
}
@Override
public void close() throws IOException {
super.close();
}
public void writeInt(long v) throws IOException {
write((int) (v & 0xff));
write((int) ((v >>> 8) & 0xff));
write((int) ((v >>> 16) & 0xff));
write((int) ((v >>> 24) & 0xff));
}
public void writeShort(int v) throws IOException {
write((v) & 0xff);
write((v >>> 8) & 0xff);
}
}
private File mInputFile;
private int mAlignment;
private File mOutputFile;
private ZipFile mZipFile;
private RandomAccessFile mRafInput;
private FilterOutputStreamEx mOutputStream;
private final List<XEntry> mXEntries = new ArrayList<>();
private long mInputFileOffset = 0;
private int mTotalPadding = 0;
public void zipAlign(File input, File output) throws IOException {
zipAlign(input, output, ALIGNMENT_4);
}
public void zipAlign(File input, File output, int alignment) throws IOException {
mInputFile = input;
mAlignment = alignment;
mOutputFile = output;
openFiles();
copyAllEntries();
buildCentralDirectory();
closeFiles();
}
private void openFiles() throws IOException {
mZipFile = new ZipFile(mInputFile);
mRafInput = new RandomAccessFile(mInputFile, "r");
mOutputStream = new FilterOutputStreamEx(new BufferedOutputStream(new FileOutputStream(mOutputFile), 32 * 1024));
}
private void copyAllEntries() throws IOException {
final int entryCount = mZipFile.size();
if (entryCount == 0) {
return;
}
final Enumeration<?> entries = mZipFile.entries();
while (entries.hasMoreElements()) {
final ZipEntry entry = (ZipEntry) entries.nextElement();
int flags = entry.getMethod() == ZipEntry.STORED ? 0 : 1 << 3;
flags |= 1 << 11;
final long outputEntryHeaderOffset = mOutputStream.totalWritten;
final int inputEntryHeaderSize = ZIP_ENTRY_HEADER_LEN + (entry.getExtra() != null ? entry.getExtra().length : 0)
+ entry.getName().getBytes(StandardCharsets.UTF_8).length;
final long inputEntryDataOffset = mInputFileOffset + inputEntryHeaderSize;
final int padding;
if (entry.getMethod() != ZipEntry.STORED) {
padding = 0;
} else {
long newOffset = inputEntryDataOffset + mTotalPadding;
padding = (int) ((mAlignment - (newOffset % mAlignment)) % mAlignment);
mTotalPadding += padding;
}
final XEntry xentry = new XEntry(entry, outputEntryHeaderOffset, flags, padding);
mXEntries.add(xentry);
byte[] extra = entry.getExtra();
if (extra == null) {
extra = new byte[padding];
} else {
byte[] newExtra = new byte[extra.length + padding];
System.arraycopy(extra, 0, newExtra, 0, extra.length);
Arrays.fill(newExtra, extra.length, newExtra.length, (byte) 0);
extra = newExtra;
}
entry.setExtra(extra);
mOutputStream.writeInt(ZipOutputStream.LOCSIG);
mOutputStream.writeShort(ZIP_ENTRY_VERSION);
mOutputStream.writeShort(flags);
mOutputStream.writeShort(entry.getMethod());
int modDate;
int time;
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(new Date(entry.getTime()));
int year = cal.get(Calendar.YEAR);
if (year < 1980) {
modDate = 0x21;
time = 0;
} else {
modDate = cal.get(Calendar.DATE);
modDate = (cal.get(Calendar.MONTH) + 1 << 5) | modDate;
modDate = ((cal.get(Calendar.YEAR) - 1980) << 9) | modDate;
time = cal.get(Calendar.SECOND) >> 1;
time = (cal.get(Calendar.MINUTE) << 5) | time;
time = (cal.get(Calendar.HOUR_OF_DAY) << 11) | time;
}
mOutputStream.writeShort(time);
mOutputStream.writeShort(modDate);
mOutputStream.writeInt(entry.getCrc());
mOutputStream.writeInt(entry.getCompressedSize());
mOutputStream.writeInt(entry.getSize());
mOutputStream.writeShort(entry.getName().getBytes(StandardCharsets.UTF_8).length);
mOutputStream.writeShort(entry.getExtra().length);
mOutputStream.write(entry.getName().getBytes(StandardCharsets.UTF_8));
mOutputStream.write(entry.getExtra(), 0, entry.getExtra().length);
mInputFileOffset += inputEntryHeaderSize;
final long sizeToCopy;
if ((flags & ZIP_ENTRY_USES_DATA_DESCR) != 0) {
sizeToCopy = (entry.isDirectory() ? 0 : entry.getCompressedSize()) + ZIP_ENTRY_DATA_DESCRIPTOR_LEN;
} else {
sizeToCopy = entry.isDirectory() ? 0 : entry.getCompressedSize();
}
if (sizeToCopy > 0) {
mRafInput.seek(mInputFileOffset);
long totalSizeCopied = 0;
final byte[] buf = new byte[32 * 1024];
while (totalSizeCopied < sizeToCopy) {
int read = mRafInput.read(buf, 0, (int) Math.min(32 * 1024, sizeToCopy - totalSizeCopied));
if (read <= 0) {
break;
}
mOutputStream.write(buf, 0, read);
totalSizeCopied += read;
}
}
mInputFileOffset += sizeToCopy;
}
}
private void buildCentralDirectory() throws IOException {
final long centralDirOffset = mOutputStream.totalWritten;
final int entryCount = mXEntries.size();
for (int i = 0; i < entryCount; i++) {
XEntry xentry = mXEntries.get(i);
final ZipEntry entry = xentry.entry;
int modDate;
int time;
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(new Date(entry.getTime()));
int year = cal.get(Calendar.YEAR);
if (year < 1980) {
modDate = 0x21;
time = 0;
} else {
modDate = cal.get(Calendar.DATE);
modDate = (cal.get(Calendar.MONTH) + 1 << 5) | modDate;
modDate = ((cal.get(Calendar.YEAR) - 1980) << 9) | modDate;
time = cal.get(Calendar.SECOND) >> 1;
time = (cal.get(Calendar.MINUTE) << 5) | time;
time = (cal.get(Calendar.HOUR_OF_DAY) << 11) | time;
}
mOutputStream.writeInt(ZipFile.CENSIG); // CEN header signature
mOutputStream.writeShort(ZIP_ENTRY_VERSION); // version made by
mOutputStream.writeShort(ZIP_ENTRY_VERSION); // version needed to extract
mOutputStream.writeShort(xentry.flags); // general purpose bit flag
mOutputStream.writeShort(entry.getMethod()); // compression method
mOutputStream.writeShort(time);
mOutputStream.writeShort(modDate);
mOutputStream.writeInt(entry.getCrc()); // crc-32
mOutputStream.writeInt(entry.getCompressedSize()); // compressed size
mOutputStream.writeInt(entry.getSize()); // uncompressed size
final byte[] nameBytes = entry.getName().getBytes(StandardCharsets.UTF_8);
mOutputStream.writeShort(nameBytes.length);
mOutputStream.writeShort(entry.getExtra() != null ? entry.getExtra().length - xentry.padding : 0);
final byte[] commentBytes;
if (entry.getComment() != null) {
commentBytes = entry.getComment().getBytes(StandardCharsets.UTF_8);
mOutputStream.writeShort(Math.min(commentBytes.length, 0xffff));
} else {
commentBytes = null;
mOutputStream.writeShort(0);
}
mOutputStream.writeShort(0); // starting disk number
mOutputStream.writeShort(0); // internal file attributes (unused)
mOutputStream.writeInt(0); // external file attributes (unused)
mOutputStream.writeInt(xentry.headerOffset); // relative offset of local header
mOutputStream.write(nameBytes);
if (entry.getExtra() != null) {
mOutputStream.write(entry.getExtra(), 0, entry.getExtra().length - xentry.padding);
}
if (commentBytes != null) {
mOutputStream.write(commentBytes, 0, Math.min(commentBytes.length, 0xffff));
}
}
final long centralDirSize = mOutputStream.totalWritten - centralDirOffset;
mOutputStream.writeInt(ZipFile.ENDSIG); // END record signature
mOutputStream.writeShort(0); // number of this disk
mOutputStream.writeShort(0); // central directory start disk
mOutputStream.writeShort(entryCount); // number of directory entries on disk
mOutputStream.writeShort(entryCount); // total number of directory entries
mOutputStream.writeInt(centralDirSize); // length of central directory
mOutputStream.writeInt(centralDirOffset); // offset of central directory
mOutputStream.writeShort(0);
mOutputStream.flush();
}
private void closeFiles() throws IOException {
try {
mZipFile.close();
} finally {
try {
mRafInput.close();
} finally {
mOutputStream.close();
}
}
}
public static void align4(File inFile) throws IOException{
align(inFile, ALIGNMENT_4);
}
public static void align4(File inFile, File outFile) throws IOException{
align(inFile, outFile, ALIGNMENT_4);
}
public static void align(File inFile, int alignment) throws IOException{
File tmp=toTmpFile(inFile);
tmp.delete();
align(inFile, tmp, alignment);
inFile.delete();
tmp.renameTo(inFile);
}
public static void align(File inFile, File outFile, int alignment) throws IOException{
ZipAlign zipAlign=new ZipAlign();
File dir=outFile.getParentFile();
if(dir!=null && !dir.exists()){
dir.mkdirs();
}
zipAlign.zipAlign(inFile, outFile, alignment);
}
private static File toTmpFile(File file){
String name=file.getName()+".align.tmp";
File dir=file.getParentFile();
if(dir==null){
return new File(name);
}
return new File(dir, name);
}
}

View File

@ -0,0 +1,93 @@
package com.reandroid.archive;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipFile;
public class ZipArchive {
private final Map<String, InputSource> mEntriesMap;
public ZipArchive(Map<String, InputSource> entriesMap){
this.mEntriesMap=entriesMap;
}
public ZipArchive(){
this(new HashMap<>());
}
public void extract(File outDir) throws IOException {
for(InputSource inputSource:listInputSources()){
extract(outDir, inputSource);
}
}
private void extract(File outDir, InputSource inputSource) throws IOException {
File file=toOutFile(outDir, inputSource.getAlias());
File dir=file.getParentFile();
if(dir!=null && !dir.exists()){
dir.mkdirs();
}
FileOutputStream outputStream=new FileOutputStream(file);
inputSource.write(outputStream);
outputStream.close();
}
private File toOutFile(File outDir, String path){
path=path.replace('/', File.separatorChar);
return new File(outDir, path);
}
public void removeDir(String dirName){
if(!dirName.endsWith("/")){
dirName=dirName+"/";
}
for(InputSource inputSource:listInputSources()){
if(inputSource.getName().startsWith(dirName)){
remove(inputSource.getName());
}
}
}
public void removeAll(Pattern patternAlias){
for(InputSource inputSource:listInputSources()){
Matcher matcher = patternAlias.matcher(inputSource.getAlias());
if(matcher.matches()){
mEntriesMap.remove(inputSource.getName());
}
}
}
public InputSource remove(String name){
InputSource inputSource=mEntriesMap.remove(name);
if(inputSource==null){
return null;
}
return inputSource;
}
public void addArchive(File archiveFile) throws IOException {
ZipFile zipFile=new ZipFile(archiveFile);
add(zipFile);
}
public void addDirectory(File dir){
addAll(InputSourceUtil.listDirectory(dir));
}
public void add(ZipFile zipFile){
List<InputSource> sourceList = InputSourceUtil.listZipFileSources(zipFile);
this.addAll(sourceList);
}
public void addAll(Collection<InputSource> inputSourceList){
for(InputSource inputSource:inputSourceList){
add(inputSource);
}
}
public void add(InputSource inputSource){
String name=inputSource.getName();
Map<String, InputSource> map=mEntriesMap;
map.remove(name);
map.put(name, inputSource);
}
public List<InputSource> listInputSources(){
return InputSourceUtil.sort(new ArrayList<>(mEntriesMap.values()));
}
public InputSource getInputSource(String name){
return mEntriesMap.get(name);
}
}

View File

@ -0,0 +1,21 @@
package com.reandroid.archive;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class ZipEntrySource extends InputSource{
private final ZipFile zipFile;
private final ZipEntry zipEntry;
public ZipEntrySource(ZipFile zipFile, ZipEntry zipEntry){
super(zipEntry.getName());
this.zipFile=zipFile;
this.zipEntry=zipEntry;
super.setMethod(zipEntry.getMethod());
}
@Override
public InputStream openStream() throws IOException {
return zipFile.getInputStream(zipEntry);
}
}

View File

@ -0,0 +1,65 @@
package com.reandroid.archive;
import java.io.*;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipSerializer {
private final List<InputSource> mSourceList;
private WriteProgress writeProgress;
public ZipSerializer(List<InputSource> sourceList){
this.mSourceList=sourceList;
}
public void setWriteProgress(WriteProgress writeProgress){
this.writeProgress=writeProgress;
}
public long writeZip(File outZip) throws IOException{
File dir=outZip.getParentFile();
if(dir!=null && !dir.exists()){
dir.mkdirs();
}
File tmp=toTmpFile(outZip);
FileOutputStream fileOutputStream=new FileOutputStream(tmp);
long length= writeZip(fileOutputStream);
fileOutputStream.close();
outZip.delete();
tmp.renameTo(outZip);
return length;
}
private File toTmpFile(File file){
File dir=file.getParentFile();
String name=file.getName()+".tmp";
return new File(dir, name);
}
public long writeZip(OutputStream outputStream) throws IOException{
long length=0;
WriteProgress progress=writeProgress;
ZipOutputStream zipOutputStream=new ZipOutputStream(outputStream);
for(InputSource inputSource:mSourceList){
if(progress!=null){
progress.onCompressFile(inputSource.getAlias(), inputSource.getMethod(), length);
}
length+=write(zipOutputStream, inputSource);
zipOutputStream.closeEntry();
}
zipOutputStream.close();
return length;
}
private long write(ZipOutputStream zipOutputStream, InputSource inputSource) throws IOException{
ZipEntry zipEntry=createZipEntry(inputSource);
zipOutputStream.putNextEntry(zipEntry);
return inputSource.write(zipOutputStream);
}
private ZipEntry createZipEntry(InputSource inputSource) throws IOException {
String name=inputSource.getAlias();
ZipEntry zipEntry=new ZipEntry(name);
int method = inputSource.getMethod();
zipEntry.setMethod(method);
if(method==ZipEntry.STORED){
zipEntry.setCrc(inputSource.getCrc());
zipEntry.setSize(inputSource.getLength());
}
return zipEntry;
}
}

View File

@ -38,7 +38,7 @@ public class ApkBundle {
tableBlock.sortPackages(); tableBlock.sortPackages();
tableBlock.refresh(); tableBlock.refresh();
} }
result.sortApkFiles(); result.getApkArchive().sortApkFiles();
return result; return result;
} }
private ApkModule getLargestTableModule() throws IOException { private ApkModule getLargestTableModule() throws IOException {

View File

@ -10,6 +10,7 @@ import com.reandroid.lib.arsc.group.StringGroup;
import com.reandroid.lib.arsc.item.TableString; import com.reandroid.lib.arsc.item.TableString;
import com.reandroid.lib.arsc.pool.TableStringPool; import com.reandroid.lib.arsc.pool.TableStringPool;
import com.reandroid.lib.arsc.value.EntryBlock; import com.reandroid.lib.arsc.value.EntryBlock;
import sun.tools.jconsole.Tab;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -60,8 +61,12 @@ public class ApkModule {
writeApk(file, null); writeApk(file, null);
} }
public void writeApk(File file, WriteProgress progress) throws IOException { public void writeApk(File file, WriteProgress progress) throws IOException {
ZipArchive archive=new ZipArchive(); uncompressNonXmlResFiles();
archive.addAll(getApkArchive().listInputSources()); APKArchive archive=getApkArchive();
InputSource table=archive.getInputSource(TableBlock.FILE_NAME);
if(table!=null){
table.setMethod(ZipEntry.STORED);
}
UncompressedFiles uf=getUncompressedFiles(); UncompressedFiles uf=getUncompressedFiles();
uf.apply(archive); uf.apply(archive);
int i=1; int i=1;
@ -75,10 +80,18 @@ public class ApkModule {
if(manifest!=null){ if(manifest!=null){
manifest.setSort(0); manifest.setSort(0);
} }
ZipSerializer serializer=new ZipSerializer(archive.listInputSources(), false); ZipSerializer serializer=new ZipSerializer(archive.listInputSources());
serializer.setWriteProgress(progress); serializer.setWriteProgress(progress);
serializer.writeZip(file); serializer.writeZip(file);
} }
private void uncompressNonXmlResFiles() throws IOException {
for(ResFile resFile:listResFiles()){
if(resFile.isBinaryXml()){
continue;
}
resFile.getInputSource().setMethod(ZipEntry.STORED);
}
}
public UncompressedFiles getUncompressedFiles(){ public UncompressedFiles getUncompressedFiles(){
return mUncompressedFiles; return mUncompressedFiles;
} }
@ -329,9 +342,6 @@ public class ApkModule {
} }
} }
} }
void sortApkFiles(){
sortApkFiles(new ArrayList<>(getApkArchive().listInputSources()));
}
public void setAPKLogger(APKLogger logger) { public void setAPKLogger(APKLogger logger) {
this.apkLogger = logger; this.apkLogger = logger;
} }
@ -361,39 +371,4 @@ public class ApkModule {
APKArchive archive=APKArchive.loadZippedApk(apkFile); APKArchive archive=APKArchive.loadZippedApk(apkFile);
return new ApkModule(moduleName, archive); return new ApkModule(moduleName, archive);
} }
private static void sortApkFiles(List<InputSource> sourceList){
Comparator<InputSource> cmp=new Comparator<InputSource>() {
@Override
public int compare(InputSource in1, InputSource in2) {
return getSortName(in1).compareTo(getSortName(in2));
}
};
sourceList.sort(cmp);
int i=0;
for(InputSource inputSource:sourceList){
inputSource.setSort(i);
i++;
}
}
private static String getSortName(InputSource inputSource){
String name=inputSource.getAlias();
StringBuilder builder=new StringBuilder();
if(name.equals(AndroidManifestBlock.FILE_NAME)){
builder.append("0 ");
}else if(name.equals(TableBlock.FILE_NAME)){
builder.append("1 ");
}else if(name.startsWith("classes")){
builder.append("2 ");
}else if(name.startsWith("res/")){
builder.append("3 ");
}else if(name.startsWith("lib/")){
builder.append("4 ");
}else if(name.startsWith("assets/")){
builder.append("5 ");
}else {
builder.append("6 ");
}
builder.append(name.toLowerCase());
return builder.toString();
}
} }

View File

@ -42,7 +42,7 @@ public class ResFile {
String typeName=typeBlock.getTypeName()+typeBlock.getResConfig().getQualifiers(); String typeName=typeBlock.getTypeName()+typeBlock.getResConfig().getQualifiers();
return root+typeName+"/"+name; return root+typeName+"/"+name;
} }
private EntryBlock pickOne(){ public EntryBlock pickOne(){
List<EntryBlock> entryList = entryBlockList; List<EntryBlock> entryList = entryBlockList;
if(entryList.size()==0){ if(entryList.size()==0){
return null; return null;
@ -100,6 +100,36 @@ public class ResFile {
jsonObject.write(file); jsonObject.write(file);
return true; return true;
} }
public File buildOutFile(File dir){
String path=buildPath();
path=path.replace('/', File.separatorChar);
return new File(dir, path);
}
public String buildPath(){
EntryBlock entryBlock=pickOne();
TypeBlock typeBlock=entryBlock.getTypeBlock();
StringBuilder builder=new StringBuilder();
builder.append(typeBlock.getTypeName());
builder.append(typeBlock.getQualifiers());
builder.append('/');
builder.append(entryBlock.getName());
String ext=getFileExtension();
if(ext!=null){
builder.append(ext);
}
return builder.toString();
}
private String getFileExtension(){
if(isBinaryXml()){
return ".xml";
}
String path=getFilePath();
int i=path.lastIndexOf('.');
if(i<0){
return null;
}
return path.substring(0);
}
@Override @Override
public String toString(){ public String toString(){
return getFilePath(); return getFilePath();

View File

@ -92,9 +92,6 @@ public abstract class StringArray<T extends StringItem> extends OffsetBlockArray
if(jsonObject==null){ if(jsonObject==null){
continue; continue;
} }
if(i>750){
i=i+0;
}
jsonArray.put(i, jsonObject); jsonArray.put(i, jsonObject);
i++; i++;
} }

View File

@ -31,8 +31,23 @@ public class BaseXmlChunk extends BaseChunk {
addChild(mNamespaceReference); addChild(mNamespaceReference);
addChild(mStringReference); addChild(mStringReference);
} }
void linkStringReferences(){
linkStringReference(mCommentReference);
linkStringReference(mNamespaceReference);
linkStringReference(mStringReference);
}
private void linkStringReference(IntegerItem item){
ResXmlString xmlString = getResXmlString(item.get());
if(xmlString!=null){
xmlString.addReferenceIfAbsent(item);
}
}
void unLinkStringReference(IntegerItem item){
ResXmlString xmlString = getResXmlString(item.get());
if(xmlString!=null){
xmlString.removeReference(item);
}
}
public void setLineNumber(int val){ public void setLineNumber(int val){
mLineNumber.set(val); mLineNumber.set(val);
} }
@ -40,19 +55,25 @@ public class BaseXmlChunk extends BaseChunk {
return mLineNumber.get(); return mLineNumber.get();
} }
public void setCommentReference(int val){ public void setCommentReference(int val){
unLinkStringReference(mCommentReference);
mCommentReference.set(val); mCommentReference.set(val);
linkStringReference(mCommentReference);
} }
public int getCommentReference(){ public int getCommentReference(){
return mCommentReference.get(); return mCommentReference.get();
} }
public void setNamespaceReference(int val){ public void setNamespaceReference(int val){
unLinkStringReference(mNamespaceReference);
mNamespaceReference.set(val); mNamespaceReference.set(val);
linkStringReference(mNamespaceReference);
} }
public int getNamespaceReference(){ public int getNamespaceReference(){
return mNamespaceReference.get(); return mNamespaceReference.get();
} }
public void setStringReference(int val){ public void setStringReference(int val){
unLinkStringReference(mStringReference);
mStringReference.set(val); mStringReference.set(val);
linkStringReference(mStringReference);
} }
public int getStringReference(){ public int getStringReference(){
return mStringReference.get(); return mStringReference.get();
@ -66,8 +87,6 @@ public class BaseXmlChunk extends BaseChunk {
setStringReference(xmlString.getIndex()); setStringReference(xmlString.getIndex());
return xmlString; return xmlString;
} }
public ResXmlStringPool getStringPool(){ public ResXmlStringPool getStringPool(){
Block parent=getParent(); Block parent=getParent();
while (parent!=null){ while (parent!=null){
@ -134,7 +153,6 @@ public class BaseXmlChunk extends BaseChunk {
setCommentReference(xmlString.getIndex()); setCommentReference(xmlString.getIndex());
} }
} }
public ResXmlElement getParentResXmlElement(){ public ResXmlElement getParentResXmlElement(){
Block parent=getParent(); Block parent=getParent();
while (parent!=null){ while (parent!=null){
@ -146,20 +164,8 @@ public class BaseXmlChunk extends BaseChunk {
return null; return null;
} }
@Override @Override
public void onReadBytes(BlockReader reader) throws IOException {
super.onReadBytes(reader);
}
@Override
protected void onChunkRefreshed() { protected void onChunkRefreshed() {
}
@Override
public void onChunkLoaded(){
super.onChunkLoaded();
if(mCommentReference.get()!=-1){
String junk=getString(mCommentReference.get());
System.out.println(junk);
}
} }
@Override @Override
public String toString(){ public String toString(){

View File

@ -3,12 +3,15 @@ package com.reandroid.lib.arsc.chunk.xml;
import com.reandroid.lib.arsc.array.ResXmlIDArray; import com.reandroid.lib.arsc.array.ResXmlIDArray;
import com.reandroid.lib.arsc.base.Block; import com.reandroid.lib.arsc.base.Block;
import com.reandroid.lib.arsc.container.FixedBlockContainer; import com.reandroid.lib.arsc.container.FixedBlockContainer;
import com.reandroid.lib.arsc.io.BlockReader;
import com.reandroid.lib.arsc.item.*; import com.reandroid.lib.arsc.item.*;
import com.reandroid.lib.arsc.pool.ResXmlStringPool; import com.reandroid.lib.arsc.pool.ResXmlStringPool;
import com.reandroid.lib.arsc.value.ValueType; import com.reandroid.lib.arsc.value.ValueType;
import com.reandroid.lib.json.JSONConvert; import com.reandroid.lib.json.JSONConvert;
import com.reandroid.lib.json.JSONObject; import com.reandroid.lib.json.JSONObject;
import java.io.IOException;
public class ResXmlAttribute extends FixedBlockContainer public class ResXmlAttribute extends FixedBlockContainer
implements Comparable<ResXmlAttribute>, JSONConvert<JSONObject> { implements Comparable<ResXmlAttribute>, JSONConvert<JSONObject> {
private final IntegerItem mNamespaceReference; private final IntegerItem mNamespaceReference;
@ -35,6 +38,20 @@ public class ResXmlAttribute extends FixedBlockContainer
addChild(5, mValueTypeByte); addChild(5, mValueTypeByte);
addChild(6, mRawValue); addChild(6, mRawValue);
} }
public void linkStringReferences(){
linkStringReference(mNamespaceReference);
linkStringReference(mNameReference);
linkStringReference(mValueStringReference);
if(getValueType()==ValueType.STRING){
linkStringReference(mRawValue);
}
}
private void linkStringReference(IntegerItem item){
ResXmlString xmlString = getResXmlString(item.get());
if(xmlString!=null){
xmlString.addReferenceIfAbsent(item);
}
}
public String getUri(){ public String getUri(){
return getString(getNamespaceReference()); return getString(getNamespaceReference());
} }

View File

@ -33,6 +33,12 @@ public class ResXmlBlock extends BaseChunk implements JSONConvert<JSONObject> {
addChild(mResXmlIDMap); addChild(mResXmlIDMap);
addChild(mResXmlElementContainer); addChild(mResXmlElementContainer);
} }
void linkStringReferences(){
ResXmlElement element=getResXmlElement();
if(element!=null){
element.linkStringReferences();
}
}
@Override @Override
public void onReadBytes(BlockReader reader) throws IOException { public void onReadBytes(BlockReader reader) throws IOException {
HeaderBlock headerBlock=reader.readHeaderBlock(); HeaderBlock headerBlock=reader.readHeaderBlock();
@ -55,6 +61,11 @@ public class ResXmlBlock extends BaseChunk implements JSONConvert<JSONObject> {
chunkReader.close(); chunkReader.close();
onChunkLoaded(); onChunkLoaded();
} }
@Override
public void onChunkLoaded(){
super.onChunkLoaded();
linkStringReferences();
}
private boolean readNext(BlockReader reader) throws IOException { private boolean readNext(BlockReader reader) throws IOException {
if(!reader.isAvailable()){ if(!reader.isAvailable()){
return false; return false;
@ -146,7 +157,6 @@ public class ResXmlBlock extends BaseChunk implements JSONConvert<JSONObject> {
} }
return jsonObject; return jsonObject;
} }
@Override @Override
public void fromJson(JSONObject json) { public void fromJson(JSONObject json) {
onFromJson(json); onFromJson(json);

View File

@ -41,6 +41,22 @@ public class ResXmlElement extends FixedBlockContainer implements JSONConvert<JS
addChild(4, mEndElementContainer); addChild(4, mEndElementContainer);
addChild(5, mEndNamespaceList); addChild(5, mEndNamespaceList);
} }
void linkStringReferences(){
for(ResXmlStartNamespace startNamespace:getStartNamespaceList()){
startNamespace.linkStringReferences();
}
ResXmlStartElement start = getStartElement();
if(start!=null){
start.linkStringReferences();
}
ResXmlText resXmlText=getResXmlText();
if(resXmlText!=null){
resXmlText.linkStringReferences();
}
for(ResXmlElement child:listElements()){
child.linkStringReferences();
}
}
public ResXmlElement createChildElement(){ public ResXmlElement createChildElement(){
return createChildElement(null); return createChildElement(null);
} }
@ -256,15 +272,12 @@ public class ResXmlElement extends FixedBlockContainer implements JSONConvert<JS
} }
return null; return null;
} }
public List<ResXmlStartNamespace> getStartNamespaceList(){ public List<ResXmlStartNamespace> getStartNamespaceList(){
return mStartNamespaceList.getChildes(); return mStartNamespaceList.getChildes();
} }
public void addStartNamespace(ResXmlStartNamespace item){ public void addStartNamespace(ResXmlStartNamespace item){
mStartNamespaceList.add(item); mStartNamespaceList.add(item);
} }
public List<ResXmlEndNamespace> getEndNamespaceList(){ public List<ResXmlEndNamespace> getEndNamespaceList(){
return mEndNamespaceList.getChildes(); return mEndNamespaceList.getChildes();
} }

View File

@ -3,6 +3,7 @@ package com.reandroid.lib.arsc.chunk.xml;
import com.reandroid.lib.arsc.chunk.ChunkType; import com.reandroid.lib.arsc.chunk.ChunkType;
import com.reandroid.lib.arsc.array.ResXmlAttributeArray; import com.reandroid.lib.arsc.array.ResXmlAttributeArray;
import com.reandroid.lib.arsc.item.ShortItem; import com.reandroid.lib.arsc.item.ShortItem;
import com.reandroid.lib.arsc.value.ValueType;
import java.util.Collection; import java.util.Collection;
@ -34,6 +35,13 @@ public class ResXmlStartElement extends BaseXmlChunk {
addChild(mAttributeArray); addChild(mAttributeArray);
} }
@Override @Override
void linkStringReferences(){
super.linkStringReferences();
for(ResXmlAttribute attr:listResXmlAttributes()){
attr.linkStringReferences();
}
}
@Override
protected void onPreRefreshRefresh(){ protected void onPreRefreshRefresh(){
sortAttributes(); sortAttributes();
} }
@ -225,8 +233,8 @@ public class ResXmlStartElement extends BaseXmlChunk {
return builder.toString(); return builder.toString();
} }
private static final short ATTRIBUTES_UNIT_SIZE=0x0014; private static final short ATTRIBUTES_UNIT_SIZE=20;
private static final short ATTRIBUTES_DEFAULT_START=0x0014; private static final short ATTRIBUTES_DEFAULT_START=20;
/* /*
* Find another way to mark an attribute is class, device actually relies on * Find another way to mark an attribute is class, device actually relies on
* value of mClassAttributePosition */ * value of mClassAttributePosition */

View File

@ -12,4 +12,12 @@ public class ResXmlStartNamespace extends ResXmlNamespace<ResXmlEndNamespace> {
public void setEnd(ResXmlEndNamespace namespace){ public void setEnd(ResXmlEndNamespace namespace){
setPair(namespace); setPair(namespace);
} }
@Override
void linkStringReferences(){
super.linkStringReferences();
ResXmlEndNamespace end = getEnd();
if(end!=null){
end.linkStringReferences();
}
}
} }

View File

@ -1,5 +1,7 @@
package com.reandroid.lib.arsc.item; package com.reandroid.lib.arsc.item;
import java.util.List;
public class ResXmlString extends StringItem { public class ResXmlString extends StringItem {
public ResXmlString(boolean utf8) { public ResXmlString(boolean utf8) {
super(utf8); super(utf8);
@ -8,4 +10,9 @@ public class ResXmlString extends StringItem {
this(utf8); this(utf8);
set(value); set(value);
} }
@Override
public String toString(){
List<ReferenceItem> refList = getReferencedList();
return "USED BY="+refList.size()+"{"+super.toString()+"}";
}
} }

View File

@ -43,6 +43,11 @@ public class StringItem extends BlockItem implements JSONConvert<JSONObject> {
mReferencedList.add(ref); mReferencedList.add(ref);
} }
} }
public void addReferenceIfAbsent(ReferenceItem ref){
if(ref!=null && !mReferencedList.contains(ref)){
mReferencedList.add(ref);
}
}
public void addReference(Collection<ReferenceItem> refList){ public void addReference(Collection<ReferenceItem> refList){
if(refList==null){ if(refList==null){
return; return;

View File

@ -366,9 +366,6 @@ public abstract class BaseStringPool<T extends StringItem> extends BaseChunk imp
int length = jsonArray.length(); int length = jsonArray.length();
List<StyledString> results=new ArrayList<>(); List<StyledString> results=new ArrayList<>();
for(int i=0;i<length;i++){ for(int i=0;i<length;i++){
if(!jsonArray.getJSONObject(i).has(StringItem.NAME_style)){
System.out.println("Dont have style at i="+i);
}
StyledString styledString=fromJson(jsonArray.getJSONObject(i)); StyledString styledString=fromJson(jsonArray.getJSONObject(i));
results.add(styledString); results.add(styledString);
} }

View File

@ -6,7 +6,6 @@ import java.util.regex.Pattern;
public class StyleBuilder { public class StyleBuilder {
public static void buildStyle(StringItem stringItem){ public static void buildStyle(StringItem stringItem){
System.out.println(stringItem.toString());
} }
public static boolean hasStyle(StringItem stringItem){ public static boolean hasStyle(StringItem stringItem){
if(stringItem==null){ if(stringItem==null){

View File

@ -1,10 +1,8 @@
package com.reandroid.lib.arsc.value; package com.reandroid.lib.arsc.value;
import com.reandroid.lib.arsc.array.EntryBlockArray;
import com.reandroid.lib.arsc.base.Block; import com.reandroid.lib.arsc.base.Block;
import com.reandroid.lib.arsc.base.BlockCounter; import com.reandroid.lib.arsc.base.BlockCounter;
import com.reandroid.lib.arsc.chunk.PackageBlock; import com.reandroid.lib.arsc.chunk.PackageBlock;
import com.reandroid.lib.arsc.chunk.SpecBlock;
import com.reandroid.lib.arsc.chunk.TableBlock; import com.reandroid.lib.arsc.chunk.TableBlock;
import com.reandroid.lib.arsc.chunk.TypeBlock; import com.reandroid.lib.arsc.chunk.TypeBlock;
import com.reandroid.lib.arsc.group.EntryGroup; import com.reandroid.lib.arsc.group.EntryGroup;
@ -19,7 +17,6 @@ import com.reandroid.lib.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
public class EntryBlock extends Block implements JSONConvert<JSONObject> { public class EntryBlock extends Block implements JSONConvert<JSONObject> {

View File

@ -4,6 +4,7 @@ import com.reandroid.lib.arsc.chunk.PackageBlock;
import com.reandroid.lib.arsc.chunk.TableBlock; import com.reandroid.lib.arsc.chunk.TableBlock;
import com.reandroid.lib.arsc.group.EntryGroup; import com.reandroid.lib.arsc.group.EntryGroup;
import com.reandroid.lib.arsc.item.TableString; import com.reandroid.lib.arsc.item.TableString;
import com.reandroid.lib.arsc.value.EntryBlock;
import java.util.*; import java.util.*;
@ -12,6 +13,24 @@ public class TableEntryStore implements EntryStore{
public TableEntryStore(){ public TableEntryStore(){
this.mPackagesMap = new HashMap<>(); this.mPackagesMap = new HashMap<>();
} }
public String getEntryName(int resourceId){
EntryBlock entryBlock=getEntryBlock(resourceId);
if(entryBlock==null){
return null;
}
return entryBlock.getName();
}
public EntryBlock getEntryBlock(int resourceId){
if(resourceId==0){
return null;
}
EntryGroup entryGroup=getEntryGroup(resourceId);
if(entryGroup==null){
return null;
}
return entryGroup.pickOne();
}
public void add(TableBlock tableBlock){ public void add(TableBlock tableBlock){
if(tableBlock==null){ if(tableBlock==null){
return; return;
@ -26,6 +45,9 @@ public class TableEntryStore implements EntryStore{
} }
byte pkgId= (byte) packageBlock.getId(); byte pkgId= (byte) packageBlock.getId();
Set<PackageBlock> packageBlockSet=getOrCreate(pkgId); Set<PackageBlock> packageBlockSet=getOrCreate(pkgId);
if(packageBlockSet.contains(packageBlock)){
return;
}
packageBlockSet.add(packageBlock); packageBlockSet.add(packageBlock);
} }
private Set<PackageBlock> getOrCreate(byte packageId){ private Set<PackageBlock> getOrCreate(byte packageId){
@ -52,9 +74,8 @@ public class TableEntryStore implements EntryStore{
} }
return results; return results;
} }
@Override @Override
public Collection<EntryGroup> getEntryGroups(int resourceId) { public List<EntryGroup> getEntryGroups(int resourceId) {
List<EntryGroup> results=new ArrayList<>(); List<EntryGroup> results=new ArrayList<>();
byte pkgId= (byte) ((resourceId>>24)&0xff); byte pkgId= (byte) ((resourceId>>24)&0xff);
Set<PackageBlock> packageBlockSet = mPackagesMap.get(pkgId); Set<PackageBlock> packageBlockSet = mPackagesMap.get(pkgId);
@ -69,7 +90,6 @@ public class TableEntryStore implements EntryStore{
} }
return results; return results;
} }
@Override @Override
public EntryGroup getEntryGroup(int resourceId) { public EntryGroup getEntryGroup(int resourceId) {
byte pkgId= (byte) ((resourceId>>24)&0xff); byte pkgId= (byte) ((resourceId>>24)&0xff);
@ -85,9 +105,8 @@ public class TableEntryStore implements EntryStore{
} }
return null; return null;
} }
@Override @Override
public Collection<PackageBlock> getPackageBlocks(byte packageId) { public List<PackageBlock> getPackageBlocks(byte packageId) {
List<PackageBlock> results=new ArrayList<>(); List<PackageBlock> results=new ArrayList<>();
Set<PackageBlock> packageBlockSet = mPackagesMap.get(packageId); Set<PackageBlock> packageBlockSet = mPackagesMap.get(packageId);
if(packageBlockSet!=null){ if(packageBlockSet!=null){
@ -95,9 +114,8 @@ public class TableEntryStore implements EntryStore{
} }
return results; return results;
} }
@Override @Override
public Collection<TableString> getTableStrings(byte packageId, int stringReference) { public List<TableString> getTableStrings(byte packageId, int stringReference) {
List<TableString> results=new ArrayList<>(); List<TableString> results=new ArrayList<>();
Set<TableBlock> tableBlockSet=getTableBlocks(packageId); Set<TableBlock> tableBlockSet=getTableBlocks(packageId);
for(TableBlock tableBlock:tableBlockSet){ for(TableBlock tableBlock:tableBlockSet){