mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-04-30 06:14:25 +02:00
V1.0.8
This commit is contained in:
parent
73dd9624cf
commit
41ed3dca11
@ -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.
76
src/main/java/com/reandroid/archive/APKArchive.java
Normal file
76
src/main/java/com/reandroid/archive/APKArchive.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
27
src/main/java/com/reandroid/archive/ByteInputSource.java
Normal file
27
src/main/java/com/reandroid/archive/ByteInputSource.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
22
src/main/java/com/reandroid/archive/FileInputSource.java
Normal file
22
src/main/java/com/reandroid/archive/FileInputSource.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
108
src/main/java/com/reandroid/archive/InputSource.java
Normal file
108
src/main/java/com/reandroid/archive/InputSource.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
107
src/main/java/com/reandroid/archive/InputSourceUtil.java
Normal file
107
src/main/java/com/reandroid/archive/InputSourceUtil.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
5
src/main/java/com/reandroid/archive/WriteProgress.java
Normal file
5
src/main/java/com/reandroid/archive/WriteProgress.java
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package com.reandroid.archive;
|
||||||
|
|
||||||
|
public interface WriteProgress {
|
||||||
|
void onCompressFile(String path, int mode, long writtenBytes);
|
||||||
|
}
|
327
src/main/java/com/reandroid/archive/ZipAlign.java
Normal file
327
src/main/java/com/reandroid/archive/ZipAlign.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
93
src/main/java/com/reandroid/archive/ZipArchive.java
Normal file
93
src/main/java/com/reandroid/archive/ZipArchive.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
21
src/main/java/com/reandroid/archive/ZipEntrySource.java
Normal file
21
src/main/java/com/reandroid/archive/ZipEntrySource.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
65
src/main/java/com/reandroid/archive/ZipSerializer.java
Normal file
65
src/main/java/com/reandroid/archive/ZipSerializer.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
@ -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(){
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -28,12 +28,12 @@ public class ResXmlElement extends FixedBlockContainer implements JSONConvert<JS
|
|||||||
private int mDepth;
|
private int mDepth;
|
||||||
public ResXmlElement() {
|
public ResXmlElement() {
|
||||||
super(6);
|
super(6);
|
||||||
this.mStartNamespaceList =new BlockList<>();
|
this.mStartNamespaceList = new BlockList<>();
|
||||||
this.mStartElementContainer=new SingleBlockContainer<>();
|
this.mStartElementContainer= new SingleBlockContainer<>();
|
||||||
this.mBody=new BlockList<>();
|
this.mBody = new BlockList<>();
|
||||||
this.mResXmlTextContainer=new SingleBlockContainer<>();
|
this.mResXmlTextContainer = new SingleBlockContainer<>();
|
||||||
this.mEndElementContainer=new SingleBlockContainer<>();
|
this.mEndElementContainer = new SingleBlockContainer<>();
|
||||||
this.mEndNamespaceList =new BlockList<>();
|
this.mEndNamespaceList = new BlockList<>();
|
||||||
addChild(0, mStartNamespaceList);
|
addChild(0, mStartNamespaceList);
|
||||||
addChild(1, mStartElementContainer);
|
addChild(1, mStartElementContainer);
|
||||||
addChild(2, mBody);
|
addChild(2, mBody);
|
||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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 */
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()+"}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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){
|
||||||
|
@ -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> {
|
||||||
|
@ -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){
|
||||||
|
Loading…
x
Reference in New Issue
Block a user