mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-04-29 22:04:25 +02:00
implement archive2
This commit is contained in:
parent
eed6c3cd24
commit
effd893de3
@ -16,6 +16,8 @@
|
||||
package com.reandroid.apk;
|
||||
|
||||
import com.reandroid.archive.*;
|
||||
import com.reandroid.archive2.Archive;
|
||||
import com.reandroid.archive2.writter.ApkWriter;
|
||||
import com.reandroid.arsc.ApkFile;
|
||||
import com.reandroid.arsc.array.PackageArray;
|
||||
import com.reandroid.arsc.chunk.Chunk;
|
||||
@ -268,10 +270,15 @@ public class ApkModule implements ApkFile {
|
||||
if(manifest!=null){
|
||||
manifest.setSort(0);
|
||||
}
|
||||
ApkWriter apkWriter = new ApkWriter(file, archive.listInputSources());
|
||||
apkWriter.write();
|
||||
apkWriter.close();
|
||||
/*
|
||||
ZipSerializer serializer=new ZipSerializer(archive.listInputSources());
|
||||
serializer.setWriteProgress(progress);
|
||||
serializer.setWriteInterceptor(interceptor);
|
||||
serializer.writeZip(file);
|
||||
*/
|
||||
}
|
||||
private void uncompressNonXmlResFiles() {
|
||||
for(ResFile resFile:listResFiles()){
|
||||
@ -725,7 +732,7 @@ public class ApkModule implements ApkFile {
|
||||
return loadApkFile(apkFile, ApkUtil.DEF_MODULE_NAME);
|
||||
}
|
||||
public static ApkModule loadApkFile(File apkFile, String moduleName) throws IOException {
|
||||
APKArchive archive=APKArchive.loadZippedApk(apkFile);
|
||||
return new ApkModule(moduleName, archive);
|
||||
Archive archive = new Archive(apkFile);
|
||||
return new ApkModule(moduleName, archive.createAPKArchive());
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ public abstract class InputSource {
|
||||
}
|
||||
private long write(OutputStream outputStream, InputStream inputStream) throws IOException {
|
||||
long result=0;
|
||||
byte[] buffer=new byte[10240];
|
||||
byte[] buffer=new byte[1024 * 1000];
|
||||
int len;
|
||||
while ((len=inputStream.read(buffer))>0){
|
||||
outputStream.write(buffer, 0, len);
|
||||
|
@ -15,10 +15,13 @@
|
||||
*/
|
||||
package com.reandroid.archive2;
|
||||
|
||||
import com.reandroid.archive.APKArchive;
|
||||
import com.reandroid.archive.InputSource;
|
||||
import com.reandroid.archive2.block.*;
|
||||
import com.reandroid.archive2.io.ArchiveFile;
|
||||
import com.reandroid.archive2.io.ArchiveEntrySource;
|
||||
import com.reandroid.archive2.io.ZipFileInput;
|
||||
import com.reandroid.archive2.io.ArchiveUtil;
|
||||
import com.reandroid.archive2.io.ZipSource;
|
||||
import com.reandroid.archive2.io.ZipInput;
|
||||
import com.reandroid.archive2.model.LocalFileDirectory;
|
||||
|
||||
import java.io.File;
|
||||
@ -26,23 +29,25 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
public class Archive {
|
||||
private final ZipSource zipSource;
|
||||
private final ZipInput zipInput;
|
||||
private final List<ArchiveEntry> entryList;
|
||||
private final EndRecord endRecord;
|
||||
private final ApkSignatureBlock apkSignatureBlock;
|
||||
public Archive(ZipSource zipSource) throws IOException {
|
||||
this.zipSource = zipSource;
|
||||
public Archive(ZipInput zipInput) throws IOException {
|
||||
this.zipInput = zipInput;
|
||||
LocalFileDirectory lfd = new LocalFileDirectory();
|
||||
lfd.visit(zipSource);
|
||||
lfd.visit(zipInput);
|
||||
List<LocalFileHeader> localFileHeaderList = lfd.getHeaderList();
|
||||
List<CentralEntryHeader> centralEntryHeaderList = lfd.getCentralFileDirectory().getHeaderList();
|
||||
List<ArchiveEntry> entryList = new ArrayList<>();
|
||||
List<ArchiveEntry> entryList = new ArrayList<>(localFileHeaderList.size());
|
||||
for(int i=0;i<localFileHeaderList.size();i++){
|
||||
LocalFileHeader lfh = localFileHeaderList.get(i);
|
||||
CentralEntryHeader ceh = centralEntryHeaderList.get(i);
|
||||
@ -54,10 +59,24 @@ public class Archive {
|
||||
this.apkSignatureBlock = lfd.getApkSigBlock();
|
||||
}
|
||||
public Archive(File file) throws IOException {
|
||||
this(new ArchiveFile(file));
|
||||
this(new ZipFileInput(file));
|
||||
}
|
||||
public APKArchive createAPKArchive(){
|
||||
return new APKArchive(mapEntrySource());
|
||||
}
|
||||
public Map<String, InputSource> mapEntrySource(){
|
||||
Map<String, InputSource> map = new LinkedHashMap<>();
|
||||
ZipInput zipInput = this.zipInput;
|
||||
List<ArchiveEntry> entryList = this.entryList;
|
||||
for(int i=0; i<entryList.size(); i++){
|
||||
ArchiveEntry entry = entryList.get(i);
|
||||
ArchiveEntrySource entrySource = new ArchiveEntrySource(zipInput, entry);
|
||||
map.put(entrySource.getAlias(), entrySource);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
public InputStream openRawInputStream(ArchiveEntry archiveEntry) throws IOException {
|
||||
return zipSource.getInputStream(archiveEntry.getFileOffset(), archiveEntry.getDataSize());
|
||||
return zipInput.getInputStream(archiveEntry.getFileOffset(), archiveEntry.getDataSize());
|
||||
}
|
||||
public InputStream openInputStream(ArchiveEntry archiveEntry) throws IOException {
|
||||
InputStream rawInputStream = openRawInputStream(archiveEntry);
|
||||
|
@ -53,7 +53,7 @@ public class ArchiveEntry extends ZipEntry {
|
||||
}
|
||||
@Override
|
||||
public long getSize() {
|
||||
return localFileHeader.getSize();
|
||||
return centralEntryHeader.getSize();
|
||||
}
|
||||
@Override
|
||||
public void setSize(long size) {
|
||||
@ -62,7 +62,7 @@ public class ArchiveEntry extends ZipEntry {
|
||||
}
|
||||
@Override
|
||||
public long getCrc() {
|
||||
return localFileHeader.getCrc();
|
||||
return centralEntryHeader.getCrc();
|
||||
}
|
||||
@Override
|
||||
public void setCrc(long crc) {
|
||||
@ -71,7 +71,7 @@ public class ArchiveEntry extends ZipEntry {
|
||||
}
|
||||
@Override
|
||||
public long getCompressedSize() {
|
||||
return localFileHeader.getCompressedSize();
|
||||
return centralEntryHeader.getCompressedSize();
|
||||
}
|
||||
@Override
|
||||
public void setCompressedSize(long csize) {
|
||||
@ -83,7 +83,7 @@ public class ArchiveEntry extends ZipEntry {
|
||||
}
|
||||
@Override
|
||||
public String getName(){
|
||||
return localFileHeader.getFileName();
|
||||
return centralEntryHeader.getFileName();
|
||||
}
|
||||
public void setName(String name){
|
||||
centralEntryHeader.setFileName(name);
|
||||
|
@ -87,6 +87,12 @@ public class CentralEntryHeader extends CommonHeader {
|
||||
setBytesLength(length, false);
|
||||
putShort(OFFSET_commentLength, value);
|
||||
}
|
||||
public long getLocalRelativeOffset(){
|
||||
return getIntegerUnsigned(OFFSET_localRelativeOffset);
|
||||
}
|
||||
public void setLocalRelativeOffset(long offset){
|
||||
putInteger(OFFSET_localRelativeOffset, offset);
|
||||
}
|
||||
@Override
|
||||
void onUtf8Changed(boolean oldValue){
|
||||
String str = mComment;
|
||||
@ -139,6 +145,7 @@ public class CentralEntryHeader extends CommonHeader {
|
||||
builder.append(", fileNameLength=").append(getFileNameLength());
|
||||
builder.append(", extraLength=").append(getExtraLength());
|
||||
builder.append(", commentLength=").append(getCommentLength());
|
||||
builder.append(", offset=").append(getLocalRelativeOffset());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@ -146,7 +153,9 @@ public class CentralEntryHeader extends CommonHeader {
|
||||
public static CentralEntryHeader fromLocalFileHeader(LocalFileHeader lfh){
|
||||
CentralEntryHeader ceh = new CentralEntryHeader();
|
||||
ceh.setSignature(ZipSignature.CENTRAL_FILE);
|
||||
ceh.setVersionMadeBy(lfh.getVersionMadeBy());
|
||||
ceh.setVersionMadeBy(0x0300);
|
||||
long offset = lfh.getFileOffset() - lfh.countBytes();
|
||||
ceh.setLocalRelativeOffset(offset);
|
||||
ceh.getGeneralPurposeFlag().setValue(lfh.getGeneralPurposeFlag().getValue());
|
||||
ceh.setMethod(lfh.getMethod());
|
||||
ceh.setDosTime(lfh.getDosTime());
|
||||
|
@ -49,6 +49,12 @@ public abstract class CommonHeader extends ZipHeader {
|
||||
}
|
||||
return getCompressedSize();
|
||||
}
|
||||
public void setDataSize(long size){
|
||||
if(getMethod() == ZipEntry.STORED){
|
||||
setSize(size);
|
||||
}
|
||||
setCompressedSize(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
int readNext(InputStream inputStream) throws IOException {
|
||||
@ -113,6 +119,8 @@ public abstract class CommonHeader extends ZipHeader {
|
||||
}
|
||||
public void setMethod(int value){
|
||||
putShort(offsetGeneralPurpose + 2, value);
|
||||
GeneralPurposeFlag gpf = getGeneralPurposeFlag();
|
||||
//gpf.setHasDataDescriptor(value != ZipEntry.STORED);
|
||||
}
|
||||
public long getDosTime(){
|
||||
return getIntegerUnsigned(offsetGeneralPurpose + 4);
|
||||
@ -353,27 +361,35 @@ public abstract class CommonHeader extends ZipHeader {
|
||||
return this.localFileHeader.getBit(offset + 1, 3);
|
||||
}
|
||||
public void setUtf8(boolean flag){
|
||||
setUtf8(flag, true);
|
||||
}
|
||||
private void setUtf8(boolean flag, boolean notify){
|
||||
boolean oldUtf8 = getUtf8();
|
||||
if(oldUtf8 == flag){
|
||||
return;
|
||||
}
|
||||
this.localFileHeader.putBit(offset +1, 3, flag);
|
||||
this.localFileHeader.onUtf8Changed(oldUtf8);
|
||||
if(notify){
|
||||
this.localFileHeader.onUtf8Changed(oldUtf8);
|
||||
}
|
||||
}
|
||||
|
||||
public int getValue(){
|
||||
return this.localFileHeader.getInteger(offset);
|
||||
return this.localFileHeader.getShortUnsigned(offset);
|
||||
}
|
||||
public void setValue(int value){
|
||||
if(value == getValue()){
|
||||
return;
|
||||
}
|
||||
boolean oldUtf8 = getUtf8();
|
||||
this.localFileHeader.putInteger(offset, value);
|
||||
this.localFileHeader.putShort(offset, value);
|
||||
if(oldUtf8 != getUtf8()){
|
||||
this.localFileHeader.onUtf8Changed(oldUtf8);
|
||||
}
|
||||
}
|
||||
public void initDefault(){
|
||||
setUtf8(false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
|
@ -49,6 +49,14 @@ public class DataDescriptor extends ZipHeader{
|
||||
builder.append(", size=").append(getSize());
|
||||
return builder.toString();
|
||||
}
|
||||
public static DataDescriptor fromLocalFile(LocalFileHeader lfh){
|
||||
DataDescriptor dataDescriptor = new DataDescriptor();
|
||||
dataDescriptor.setSignature(ZipSignature.DATA_DESCRIPTOR);
|
||||
dataDescriptor.setSize(lfh.getSize());
|
||||
dataDescriptor.setCompressedSize(lfh.getCompressedSize());
|
||||
dataDescriptor.setCrc(lfh.getCrc());
|
||||
return dataDescriptor;
|
||||
}
|
||||
|
||||
private static final int OFFSET_crc = 4;
|
||||
private static final int OFFSET_compressed_size = 8;
|
||||
|
@ -87,9 +87,6 @@ public abstract class LengthPrefixedList<T extends Block> extends FixedBlockCont
|
||||
}
|
||||
BlockReader chunkReader = reader.create(totalSize);
|
||||
readElements(chunkReader);
|
||||
if(chunkReader.isAvailable()){
|
||||
String junk = "";
|
||||
}
|
||||
bottomContainer.readBytes(chunkReader);
|
||||
reader.offset(totalSize);
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ package com.reandroid.archive2.block;
|
||||
|
||||
import com.reandroid.archive2.ZipSignature;
|
||||
|
||||
|
||||
public class LocalFileHeader extends CommonHeader {
|
||||
private DataDescriptor dataDescriptor;
|
||||
public LocalFileHeader(){
|
||||
@ -48,6 +47,7 @@ public class LocalFileHeader extends CommonHeader {
|
||||
}
|
||||
public void setDataDescriptor(DataDescriptor dataDescriptor){
|
||||
this.dataDescriptor = dataDescriptor;
|
||||
getGeneralPurposeFlag().setHasDataDescriptor(dataDescriptor!=null);
|
||||
}
|
||||
|
||||
public static LocalFileHeader fromCentralEntryHeader(CentralEntryHeader ceh){
|
||||
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.io;
|
||||
|
||||
import com.reandroid.archive.InputSource;
|
||||
import com.reandroid.archive2.ArchiveEntry;
|
||||
import com.reandroid.archive2.block.LocalFileHeader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
public class ArchiveEntrySource extends InputSource {
|
||||
private final ZipInput zipInput;
|
||||
private final ArchiveEntry archiveEntry;
|
||||
public ArchiveEntrySource(ZipInput zipInput, ArchiveEntry archiveEntry){
|
||||
super(archiveEntry.getName());
|
||||
this.zipInput = zipInput;
|
||||
this.archiveEntry = archiveEntry;
|
||||
setMethod(archiveEntry.getMethod());
|
||||
}
|
||||
public FileChannel getFileChannel() throws IOException {
|
||||
FileChannel fileChannel = getZipSource().getFileChannel();
|
||||
fileChannel.position(getFileOffset());
|
||||
return fileChannel;
|
||||
}
|
||||
public ZipInput getZipSource(){
|
||||
return zipInput;
|
||||
}
|
||||
public ArchiveEntry getArchiveEntry() {
|
||||
return archiveEntry;
|
||||
}
|
||||
public long getFileOffset(){
|
||||
return getArchiveEntry().getFileOffset();
|
||||
}
|
||||
@Override
|
||||
public long getLength() throws IOException{
|
||||
return getArchiveEntry().getDataSize();
|
||||
}
|
||||
@Override
|
||||
public long getCrc() throws IOException{
|
||||
return getArchiveEntry().getCrc();
|
||||
}
|
||||
@Override
|
||||
public InputStream openStream() throws IOException {
|
||||
ArchiveEntry archiveEntry = getArchiveEntry();
|
||||
LocalFileHeader lfh = archiveEntry.getLocalFileHeader();
|
||||
InputStream inputStream = getZipSource().getInputStream(
|
||||
archiveEntry.getFileOffset(), archiveEntry.getDataSize());
|
||||
if(lfh.getSize() == lfh.getCompressedSize()){
|
||||
return inputStream;
|
||||
}
|
||||
return new InflaterInputStream(inputStream,
|
||||
new Inflater(true), 512);
|
||||
}
|
||||
}
|
129
src/main/java/com/reandroid/archive2/io/CountingInputStream.java
Normal file
129
src/main/java/com/reandroid/archive2/io/CountingInputStream.java
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
public class CountingInputStream<T extends InputStream> extends InputStream {
|
||||
private final T inputStream;
|
||||
private final CRC32 crc;
|
||||
private long size;
|
||||
private long mCheckSum;
|
||||
private boolean mFinished;
|
||||
public CountingInputStream(T inputStream, boolean disableCrc){
|
||||
this.inputStream = inputStream;
|
||||
CRC32 crc32;
|
||||
if(disableCrc){
|
||||
crc32 = null;
|
||||
}else {
|
||||
crc32 = new CRC32();
|
||||
}
|
||||
this.crc = crc32;
|
||||
}
|
||||
public CountingInputStream(T inputStream){
|
||||
this(inputStream, false);
|
||||
}
|
||||
public T getInputStream() {
|
||||
return inputStream;
|
||||
}
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
public long getCrc() {
|
||||
return mCheckSum;
|
||||
}
|
||||
@Override
|
||||
public int read(byte[] bytes, int offset, int length) throws IOException{
|
||||
if(mFinished){
|
||||
return -1;
|
||||
}
|
||||
length = inputStream.read(bytes, offset, length);
|
||||
if(length < 0){
|
||||
onFinished();
|
||||
return length;
|
||||
}
|
||||
this.size += length;
|
||||
if(this.crc != null){
|
||||
this.crc.update(bytes, offset, length);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
@Override
|
||||
public int read(byte[] bytes) throws IOException{
|
||||
return this.read(bytes, 0, bytes.length);
|
||||
}
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
throw new IOException("Why one byte ?");
|
||||
}
|
||||
@Override
|
||||
public long skip(long amount) throws IOException {
|
||||
if(mFinished){
|
||||
return 0;
|
||||
}
|
||||
if(amount <= 0){
|
||||
return amount;
|
||||
}
|
||||
InputStream inputStream = this.inputStream;
|
||||
if(inputStream instanceof CountingInputStream){
|
||||
return inputStream.skip(amount);
|
||||
}
|
||||
long remaining = amount;
|
||||
int len = 1024 * 1000;
|
||||
if(remaining < len){
|
||||
len = (int) remaining;
|
||||
}
|
||||
final byte[] buffer = new byte[len];
|
||||
int read;
|
||||
while (true){
|
||||
read = inputStream.read(buffer, 0, len);
|
||||
if(read < 0){
|
||||
onFinished();
|
||||
break;
|
||||
}
|
||||
remaining = remaining - read;
|
||||
if(remaining <= 0){
|
||||
break;
|
||||
}
|
||||
if(remaining < len){
|
||||
len = (int) remaining;
|
||||
}
|
||||
}
|
||||
return amount - remaining;
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException{
|
||||
if(!mFinished){
|
||||
onFinished();
|
||||
}
|
||||
inputStream.close();
|
||||
}
|
||||
private void onFinished(){
|
||||
this.mFinished = true;
|
||||
if(this.crc!=null){
|
||||
this.mCheckSum = this.crc.getValue();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
if(!mFinished || crc==null){
|
||||
return "[" + size + "]: " + inputStream.getClass().getSimpleName();
|
||||
}
|
||||
return "[size=" + size +", crc=" + String.format("0x%08x", mCheckSum) + "]: " + inputStream.getClass().getSimpleName();
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
public class CountingOutputStream<T extends OutputStream> extends OutputStream {
|
||||
private final T outputStream;
|
||||
private CRC32 crc;
|
||||
private long size;
|
||||
public CountingOutputStream(T outputStream, boolean disableCrc){
|
||||
this.outputStream = outputStream;
|
||||
CRC32 crc32;
|
||||
if(disableCrc){
|
||||
crc32 = null;
|
||||
}else {
|
||||
crc32 = new CRC32();
|
||||
}
|
||||
this.crc = crc32;
|
||||
}
|
||||
public CountingOutputStream(T outputStream){
|
||||
this(outputStream, false);
|
||||
}
|
||||
|
||||
public void disableCrc(boolean disableCrc) {
|
||||
if(!disableCrc){
|
||||
if(crc == null){
|
||||
this.crc = new CRC32();
|
||||
}
|
||||
}else{
|
||||
this.crc = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void reset(){
|
||||
this.crc = new CRC32();
|
||||
this.size = 0L;
|
||||
}
|
||||
public T getOutputStream() {
|
||||
return outputStream;
|
||||
}
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
public long getCrc() {
|
||||
if(crc != null){
|
||||
return crc.getValue();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public void write(byte[] bytes, int offset, int length) throws IOException{
|
||||
if(length == 0){
|
||||
return;
|
||||
}
|
||||
outputStream.write(bytes, offset, length);
|
||||
this.size += length;
|
||||
if(this.crc != null){
|
||||
this.crc.update(bytes, offset, length);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void write(byte[] bytes) throws IOException{
|
||||
this.write(bytes, 0, bytes.length);
|
||||
}
|
||||
@Override
|
||||
public void write(int i) throws IOException {
|
||||
this.write(new byte[]{(byte) i}, 0, 1);
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException{
|
||||
outputStream.close();
|
||||
}
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
outputStream.flush();
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return "[" + size + "]: " + outputStream.getClass().getSimpleName();
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
public class FileChannelInputStream extends InputStream {
|
||||
private final FileChannel fileChannel;
|
||||
private final long length;
|
||||
private long total;
|
||||
private final byte[] buffer;
|
||||
private int pos;
|
||||
private int bufferLength;
|
||||
|
||||
public FileChannelInputStream(FileChannel fileChannel, long length){
|
||||
this.fileChannel = fileChannel;
|
||||
this.length = length;
|
||||
int len = 1024 * 1000 * 100;
|
||||
if(length < len){
|
||||
len = (int) length;
|
||||
}
|
||||
this.buffer = new byte[len];
|
||||
this.bufferLength = len;
|
||||
this.pos = len;
|
||||
}
|
||||
@Override
|
||||
public int read(byte[] bytes) throws IOException {
|
||||
return read(bytes, 0, bytes.length);
|
||||
}
|
||||
@Override
|
||||
public int read(byte[] bytes, int offset, int length) throws IOException {
|
||||
if(isFinished()){
|
||||
return -1;
|
||||
}
|
||||
if(length==0){
|
||||
return 0;
|
||||
}
|
||||
loadBuffer();
|
||||
int result = 0;
|
||||
int read = readBuffer(bytes, offset, length);
|
||||
result += read;
|
||||
length = length - read;
|
||||
offset = offset + read;
|
||||
while (length>0 && !isFinished()){
|
||||
loadBuffer();
|
||||
read = readBuffer(bytes, offset, length);
|
||||
result += read;
|
||||
length = length - read;
|
||||
offset = offset + read;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private int readBuffer(byte[] bytes, int offset, int length){
|
||||
int avail = bufferLength - pos;
|
||||
if(avail == 0){
|
||||
return 0;
|
||||
}
|
||||
int read = length;
|
||||
if(read > avail){
|
||||
read = avail;
|
||||
}
|
||||
System.arraycopy(buffer, pos, bytes, offset, read);
|
||||
pos += read;
|
||||
total += read;
|
||||
return read;
|
||||
}
|
||||
private void loadBuffer() throws IOException {
|
||||
byte[] buffer = this.buffer;
|
||||
if(this.pos < buffer.length){
|
||||
return;
|
||||
}
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
|
||||
bufferLength = fileChannel.read(byteBuffer);
|
||||
pos = 0;
|
||||
}
|
||||
private boolean isFinished(){
|
||||
return total >= length;
|
||||
}
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
throw new IOException("Why one byte?");
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
public class FileChannelOutputStream extends OutputStream {
|
||||
private final FileChannel fileChannel;
|
||||
public FileChannelOutputStream(FileChannel fileChannel){
|
||||
this.fileChannel = fileChannel;
|
||||
}
|
||||
@Override
|
||||
public void write(byte[] bytes) throws IOException {
|
||||
write(bytes, 0, bytes.length);
|
||||
}
|
||||
@Override
|
||||
public void write(byte[] bytes, int offset, int length) throws IOException {
|
||||
long position = fileChannel.position();
|
||||
length = fileChannel.write(ByteBuffer.wrap(bytes, offset, length));
|
||||
fileChannel.position(position + length);
|
||||
}
|
||||
@Override
|
||||
public void write(int i) throws IOException {
|
||||
byte b = (byte) (i & 0xff);
|
||||
write(new byte[]{b});
|
||||
}
|
||||
@Override
|
||||
public void close(){
|
||||
|
||||
}
|
||||
}
|
30
src/main/java/com/reandroid/archive2/io/RandomStream.java
Normal file
30
src/main/java/com/reandroid/archive2/io/RandomStream.java
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.Channel;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
public interface RandomStream extends Channel {
|
||||
long position() throws IOException;
|
||||
void position(long pos) throws IOException;
|
||||
@Override
|
||||
void close() throws IOException;
|
||||
@Override
|
||||
boolean isOpen();
|
||||
FileChannel getFileChannel() throws IOException;
|
||||
}
|
24
src/main/java/com/reandroid/archive2/io/ReadOnlyStream.java
Normal file
24
src/main/java/com/reandroid/archive2/io/ReadOnlyStream.java
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public interface ReadOnlyStream extends RandomStream{
|
||||
long getLength() throws IOException;
|
||||
InputStream getInputStream(long offset, long length) throws IOException;
|
||||
}
|
26
src/main/java/com/reandroid/archive2/io/WriteOnlyStream.java
Normal file
26
src/main/java/com/reandroid/archive2/io/WriteOnlyStream.java
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public interface WriteOnlyStream extends RandomStream{
|
||||
void write(ReadOnlyStream readStream, long length) throws IOException;
|
||||
void write(InputStream inputStream) throws IOException;
|
||||
OutputStream getOutputStream() throws IOException;
|
||||
}
|
@ -20,17 +20,39 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
public class ArchiveFile extends ZipSource{
|
||||
public class ZipFileInput extends ZipInput {
|
||||
private final File file;
|
||||
private FileChannel fileChannel;
|
||||
private SlicedInputStream mCurrentInputStream;
|
||||
public ArchiveFile(File file){
|
||||
private InputStream mCurrentInputStream;
|
||||
public ZipFileInput(File file){
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long position() throws IOException {
|
||||
FileChannel fileChannel = this.fileChannel;
|
||||
if(fileChannel != null){
|
||||
return fileChannel.position();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public void position(long pos) throws IOException {
|
||||
getFileChannel().position(pos);
|
||||
}
|
||||
@Override
|
||||
public long getLength(){
|
||||
return this.file.length();
|
||||
}
|
||||
@Override
|
||||
public InputStream getInputStream(long offset, long length) throws IOException {
|
||||
closeCurrentInputStream();
|
||||
FileChannel fileChannel = getFileChannel();
|
||||
fileChannel.position(offset);
|
||||
mCurrentInputStream = new FileChannelInputStream(fileChannel, length);
|
||||
return mCurrentInputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getFooter(int minLength) throws IOException {
|
||||
long position = getLength();
|
||||
@ -45,16 +67,7 @@ public class ArchiveFile extends ZipSource{
|
||||
return buffer.array();
|
||||
}
|
||||
@Override
|
||||
public InputStream getInputStream(long offset, long length) throws IOException {
|
||||
close();
|
||||
mCurrentInputStream = new SlicedInputStream(new FileInputStream(this.file), offset, length);
|
||||
return mCurrentInputStream;
|
||||
}
|
||||
@Override
|
||||
public OutputStream getOutputStream(long offset) throws IOException {
|
||||
return null;
|
||||
}
|
||||
private FileChannel getFileChannel() throws IOException {
|
||||
public FileChannel getFileChannel() throws IOException {
|
||||
FileChannel fileChannel = this.fileChannel;
|
||||
if(fileChannel != null){
|
||||
return fileChannel;
|
||||
@ -67,8 +80,18 @@ public class ArchiveFile extends ZipSource{
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
closeChannel();
|
||||
closeCurrentInputStream();
|
||||
closeChannel();
|
||||
}
|
||||
@Override
|
||||
public boolean isOpen(){
|
||||
FileChannel fileChannel = this.fileChannel;
|
||||
if(fileChannel == null){
|
||||
return false;
|
||||
}
|
||||
synchronized (this){
|
||||
return fileChannel.isOpen();
|
||||
}
|
||||
}
|
||||
private void closeChannel() throws IOException {
|
||||
FileChannel fileChannel = this.fileChannel;
|
||||
@ -81,7 +104,7 @@ public class ArchiveFile extends ZipSource{
|
||||
}
|
||||
}
|
||||
private void closeCurrentInputStream() throws IOException {
|
||||
SlicedInputStream current = this.mCurrentInputStream;
|
||||
InputStream current = this.mCurrentInputStream;
|
||||
if(current == null){
|
||||
return;
|
||||
}
|
127
src/main/java/com/reandroid/archive2/io/ZipFileOutput.java
Normal file
127
src/main/java/com/reandroid/archive2/io/ZipFileOutput.java
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
public class ZipFileOutput extends ZipOutput{
|
||||
private final File file;
|
||||
private FileChannel fileChannel;
|
||||
private FileChannelOutputStream outputStream;
|
||||
public ZipFileOutput(File file) throws IOException {
|
||||
initFile(file);
|
||||
this.file = file;
|
||||
}
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
public void write(FileChannel input, long length) throws IOException{
|
||||
FileChannel fileChannel = getFileChannel();
|
||||
long pos = fileChannel.position();
|
||||
length = fileChannel.transferFrom(input, pos, length);
|
||||
fileChannel.position(pos + length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long position() throws IOException {
|
||||
return getFileChannel().position();
|
||||
}
|
||||
@Override
|
||||
public void position(long pos) throws IOException {
|
||||
getFileChannel().position(pos);
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
FileChannel fileChannel = this.fileChannel;
|
||||
if(fileChannel != null){
|
||||
fileChannel.close();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
FileChannel fileChannel = this.fileChannel;
|
||||
if(fileChannel != null){
|
||||
return fileChannel.isOpen();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public FileChannel getFileChannel() throws IOException {
|
||||
FileChannel fileChannel = this.fileChannel;
|
||||
if(fileChannel != null){
|
||||
return fileChannel;
|
||||
}
|
||||
synchronized (this){
|
||||
fileChannel = FileChannel.open(this.file.toPath(), StandardOpenOption.WRITE);
|
||||
this.fileChannel = fileChannel;
|
||||
return fileChannel;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void write(ReadOnlyStream readStream, long length) throws IOException {
|
||||
FileChannel input = readStream.getFileChannel();
|
||||
if(input != null){
|
||||
write(input, length);
|
||||
return;
|
||||
}
|
||||
write(readStream.getInputStream(readStream.position(), length));
|
||||
}
|
||||
@Override
|
||||
public void write(InputStream inputStream) throws IOException {
|
||||
FileChannel fileChannel = getFileChannel();
|
||||
long pos = fileChannel.position();
|
||||
int bufferLength = 1024 * 1000 * 100;
|
||||
byte[] buffer = new byte[bufferLength];
|
||||
long result = 0;
|
||||
int read;
|
||||
while ((read = inputStream.read(buffer, 0, bufferLength)) > 0){
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, 0, read);
|
||||
fileChannel.write(byteBuffer);
|
||||
result += read;
|
||||
}
|
||||
inputStream.close();
|
||||
fileChannel.position(pos + result);
|
||||
}
|
||||
@Override
|
||||
public FileChannelOutputStream getOutputStream() throws IOException {
|
||||
FileChannelOutputStream outputStream = this.outputStream;
|
||||
if(outputStream == null){
|
||||
outputStream = new FileChannelOutputStream(getFileChannel());
|
||||
this.outputStream = outputStream;
|
||||
}
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
|
||||
private static void initFile(File file) throws IOException{
|
||||
if(file.isDirectory()){
|
||||
throw new IOException("Not file: " + file);
|
||||
}
|
||||
File dir = file.getParentFile();
|
||||
if(dir != null && !dir.exists()){
|
||||
dir.mkdirs();
|
||||
}
|
||||
if(file.exists()){
|
||||
file.delete();
|
||||
}
|
||||
file.createNewFile();
|
||||
}
|
||||
}
|
@ -15,14 +15,8 @@
|
||||
*/
|
||||
package com.reandroid.archive2.io;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public abstract class ZipSource implements Closeable {
|
||||
public abstract long getLength();
|
||||
public abstract class ZipInput implements ReadOnlyStream {
|
||||
public abstract byte[] getFooter(int minLength) throws IOException;
|
||||
public abstract InputStream getInputStream(long offset, long length) throws IOException;
|
||||
public abstract OutputStream getOutputStream(long offset) throws IOException;
|
||||
}
|
20
src/main/java/com/reandroid/archive2/io/ZipOutput.java
Normal file
20
src/main/java/com/reandroid/archive2/io/ZipOutput.java
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.io;
|
||||
|
||||
public abstract class ZipOutput implements WriteOnlyStream{
|
||||
|
||||
}
|
@ -19,7 +19,7 @@ import com.reandroid.archive2.block.CentralEntryHeader;
|
||||
import com.reandroid.archive2.block.EndRecord;
|
||||
import com.reandroid.archive2.block.LocalFileHeader;
|
||||
import com.reandroid.archive2.block.SignatureFooter;
|
||||
import com.reandroid.archive2.io.ZipSource;
|
||||
import com.reandroid.archive2.io.ZipInput;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
@ -73,13 +73,13 @@ public class CentralFileDirectory {
|
||||
public EndRecord getEndRecord() {
|
||||
return endRecord;
|
||||
}
|
||||
public void visit(ZipSource zipSource) throws IOException {
|
||||
byte[] footer = zipSource.getFooter(SignatureFooter.MIN_SIZE + EndRecord.MAX_LENGTH);
|
||||
public void visit(ZipInput zipInput) throws IOException {
|
||||
byte[] footer = zipInput.getFooter(SignatureFooter.MIN_SIZE + EndRecord.MAX_LENGTH);
|
||||
EndRecord endRecord = findEndRecord(footer);
|
||||
int length = (int) endRecord.getLengthOfCentralDirectory();
|
||||
int endLength = endRecord.countBytes();
|
||||
if(footer.length < (length + endLength)){
|
||||
footer = zipSource.getFooter(SignatureFooter.MIN_SIZE + length + endLength);
|
||||
footer = zipInput.getFooter(SignatureFooter.MIN_SIZE + length + endLength);
|
||||
}
|
||||
int offset = footer.length - length - endLength;
|
||||
this.endRecord = endRecord;
|
||||
|
@ -17,7 +17,7 @@ package com.reandroid.archive2.model;
|
||||
|
||||
import com.reandroid.archive2.block.*;
|
||||
import com.reandroid.archive2.block.ApkSignatureBlock;
|
||||
import com.reandroid.archive2.io.ZipSource;
|
||||
import com.reandroid.archive2.io.ZipInput;
|
||||
import com.reandroid.arsc.io.BlockReader;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -37,14 +37,14 @@ public class LocalFileDirectory {
|
||||
public LocalFileDirectory(){
|
||||
this(new CentralFileDirectory());
|
||||
}
|
||||
public void visit(ZipSource zipSource) throws IOException {
|
||||
getCentralFileDirectory().visit(zipSource);
|
||||
visitLocalFile(zipSource);
|
||||
visitApkSigBlock(zipSource);
|
||||
public void visit(ZipInput zipInput) throws IOException {
|
||||
getCentralFileDirectory().visit(zipInput);
|
||||
visitLocalFile(zipInput);
|
||||
visitApkSigBlock(zipInput);
|
||||
}
|
||||
private void visitLocalFile(ZipSource zipSource) throws IOException {
|
||||
private void visitLocalFile(ZipInput zipInput) throws IOException {
|
||||
EndRecord endRecord = getCentralFileDirectory().getEndRecord();
|
||||
InputStream inputStream = zipSource.getInputStream(0, endRecord.getOffsetOfCentralDirectory());
|
||||
InputStream inputStream = zipInput.getInputStream(0, endRecord.getOffsetOfCentralDirectory());
|
||||
visitLocalFile(inputStream);
|
||||
inputStream.close();
|
||||
}
|
||||
@ -81,7 +81,7 @@ public class LocalFileDirectory {
|
||||
}
|
||||
mTotalDataLength = offset;
|
||||
}
|
||||
private void visitApkSigBlock(ZipSource zipSource) throws IOException{
|
||||
private void visitApkSigBlock(ZipInput zipInput) throws IOException{
|
||||
CentralFileDirectory cfd = getCentralFileDirectory();
|
||||
SignatureFooter footer = cfd.getSignatureFooter();
|
||||
if(footer == null || !footer.isValid()){
|
||||
@ -91,7 +91,7 @@ public class LocalFileDirectory {
|
||||
long length = footer.getSignatureSize() + 8;
|
||||
long offset = endRecord.getOffsetOfCentralDirectory() - length;
|
||||
ApkSignatureBlock apkSignatureBlock = new ApkSignatureBlock(footer);
|
||||
apkSignatureBlock.readBytes(new BlockReader(zipSource.getInputStream(offset, length)));
|
||||
apkSignatureBlock.readBytes(new BlockReader(zipInput.getInputStream(offset, length)));
|
||||
this.apkSignatureBlock = apkSignatureBlock;
|
||||
}
|
||||
public ApkSignatureBlock getApkSigBlock() {
|
||||
|
116
src/main/java/com/reandroid/archive2/writter/ApkWriter.java
Normal file
116
src/main/java/com/reandroid/archive2/writter/ApkWriter.java
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.writter;
|
||||
|
||||
import com.reandroid.apk.RenamedInputSource;
|
||||
import com.reandroid.archive.InputSource;
|
||||
import com.reandroid.archive2.ZipSignature;
|
||||
import com.reandroid.archive2.block.EndRecord;
|
||||
import com.reandroid.archive2.io.ArchiveEntrySource;
|
||||
import com.reandroid.archive2.io.ZipFileOutput;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ApkWriter extends ZipFileOutput {
|
||||
private final Collection<? extends InputSource> sourceList;
|
||||
public ApkWriter(File file, Collection<? extends InputSource> sourceList) throws IOException {
|
||||
super(file);
|
||||
this.sourceList = sourceList;
|
||||
}
|
||||
public void write()throws IOException {
|
||||
List<OutputSource> outputList = buildOutputEntry();
|
||||
BufferFileInput buffer = writeBuffer(outputList);
|
||||
buffer.unlock();
|
||||
ZipAligner aligner = new ZipAligner();
|
||||
align(aligner, outputList);
|
||||
writeApk(outputList);
|
||||
writeCEH(outputList);
|
||||
buffer.close();
|
||||
}
|
||||
private void writeCEH(List<OutputSource> outputList) throws IOException{
|
||||
EndRecord endRecord = new EndRecord();
|
||||
endRecord.setSignature(ZipSignature.END_RECORD);
|
||||
long offset = position();
|
||||
endRecord.setOffsetOfCentralDirectory((int) offset);
|
||||
endRecord.setNumberOfDirectories(outputList.size());
|
||||
endRecord.setTotalNumberOfDirectories(outputList.size());
|
||||
for(OutputSource outputSource:outputList){
|
||||
outputSource.writeCEH(this);
|
||||
}
|
||||
long len = position() - offset;
|
||||
endRecord.setLengthOfCentralDirectory(len);
|
||||
endRecord.writeBytes(getOutputStream());
|
||||
}
|
||||
private void writeApk(List<OutputSource> outputList) throws IOException{
|
||||
for(OutputSource outputSource:outputList){
|
||||
outputSource.writeApk( this);
|
||||
}
|
||||
}
|
||||
private BufferFileInput writeBuffer(List<OutputSource> outputList) throws IOException {
|
||||
File bufferFile = getBufferFile();
|
||||
BufferFileOutput output = new BufferFileOutput(bufferFile);
|
||||
BufferFileInput input = new BufferFileInput(bufferFile);
|
||||
for(OutputSource outputSource:outputList){
|
||||
outputSource.makeBuffer(input, output);
|
||||
}
|
||||
output.close();
|
||||
return input;
|
||||
}
|
||||
private void align(ZipAligner aligner, List<OutputSource> outputList) throws IOException{
|
||||
for(OutputSource outputSource:outputList){
|
||||
outputSource.align(aligner);
|
||||
}
|
||||
}
|
||||
private File getBufferFile(){
|
||||
File file = getFile();
|
||||
File dir = file.getParentFile();
|
||||
String name = file.getAbsolutePath();
|
||||
name = "tmp" + name.hashCode();
|
||||
File bufFile;
|
||||
if(dir != null){
|
||||
bufFile = new File(dir, name);
|
||||
}else {
|
||||
bufFile = new File(name);
|
||||
}
|
||||
bufFile.deleteOnExit();
|
||||
return bufFile;
|
||||
}
|
||||
private List<OutputSource> buildOutputEntry(){
|
||||
Collection<? extends InputSource> sourceList = this.sourceList;
|
||||
List<OutputSource> results = new ArrayList<>(sourceList.size());
|
||||
for(InputSource inputSource:sourceList){
|
||||
results.add(toOutputSource(inputSource));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
private OutputSource toOutputSource(InputSource inputSource){
|
||||
if(inputSource instanceof ArchiveEntrySource){
|
||||
return new ArchiveOutputSource(inputSource);
|
||||
}
|
||||
if(inputSource instanceof RenamedInputSource){
|
||||
InputSource renamed = ((RenamedInputSource<?>) inputSource).getInputSource();
|
||||
if(renamed instanceof ArchiveEntrySource){
|
||||
return new RenamedArchiveSource((RenamedInputSource<?>) inputSource);
|
||||
}
|
||||
}
|
||||
return new OutputSource(inputSource);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.writter;
|
||||
|
||||
import com.reandroid.archive.InputSource;
|
||||
import com.reandroid.archive2.block.LocalFileHeader;
|
||||
import com.reandroid.archive2.io.ArchiveEntrySource;
|
||||
import com.reandroid.archive2.io.ZipFileInput;
|
||||
import com.reandroid.archive2.io.ZipInput;
|
||||
|
||||
|
||||
public class ArchiveOutputSource extends OutputSource{
|
||||
public ArchiveOutputSource(InputSource inputSource){
|
||||
super(inputSource);
|
||||
}
|
||||
|
||||
ArchiveEntrySource getArchiveSource(){
|
||||
return (ArchiveEntrySource) super.getInputSource();
|
||||
}
|
||||
@Override
|
||||
EntryBuffer makeFromEntry(){
|
||||
ArchiveEntrySource entrySource = getArchiveSource();
|
||||
ZipInput zip = entrySource.getZipSource();
|
||||
if(!(zip instanceof ZipFileInput)){
|
||||
return null;
|
||||
}
|
||||
LocalFileHeader lfh = entrySource.getArchiveEntry().getLocalFileHeader();
|
||||
if(lfh.getMethod() != getInputSource().getMethod()){
|
||||
return null;
|
||||
}
|
||||
return new EntryBuffer((ZipFileInput) zip,
|
||||
lfh.getFileOffset(),
|
||||
lfh.getDataSize());
|
||||
}
|
||||
@Override
|
||||
public LocalFileHeader createLocalFileHeader(){
|
||||
ArchiveEntrySource source = getArchiveSource();
|
||||
return source.getArchiveEntry().getLocalFileHeader();
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.writter;
|
||||
|
||||
import com.reandroid.archive2.io.ZipFileInput;
|
||||
import com.reandroid.archive2.io.ZipInput;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
public class BufferFileInput extends ZipFileInput {
|
||||
private boolean unlocked;
|
||||
public BufferFileInput(File file){
|
||||
super(file);
|
||||
}
|
||||
|
||||
public void unlock(){
|
||||
this.unlocked = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileChannel getFileChannel() throws IOException {
|
||||
if(unlocked){
|
||||
return super.getFileChannel();
|
||||
}
|
||||
throw new IOException("File locked!");
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.writter;
|
||||
|
||||
import com.reandroid.archive2.io.ZipFileOutput;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class BufferFileOutput extends ZipFileOutput {
|
||||
public BufferFileOutput(File file) throws IOException {
|
||||
super(file);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.writter;
|
||||
|
||||
import com.reandroid.archive2.io.ZipFileInput;
|
||||
|
||||
public class EntryBuffer {
|
||||
private final ZipFileInput zipFileInput;
|
||||
private final long offset;
|
||||
private final long length;
|
||||
public EntryBuffer(ZipFileInput zipFileInput, long offset, long length){
|
||||
this.zipFileInput = zipFileInput;
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public ZipFileInput getZipFileInput() {
|
||||
return zipFileInput;
|
||||
}
|
||||
public long getOffset() {
|
||||
return offset;
|
||||
}
|
||||
public long getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
}
|
154
src/main/java/com/reandroid/archive2/writter/OutputSource.java
Normal file
154
src/main/java/com/reandroid/archive2/writter/OutputSource.java
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.writter;
|
||||
|
||||
import com.reandroid.archive.InputSource;
|
||||
import com.reandroid.archive2.ZipSignature;
|
||||
import com.reandroid.archive2.block.CentralEntryHeader;
|
||||
import com.reandroid.archive2.block.DataDescriptor;
|
||||
import com.reandroid.archive2.block.LocalFileHeader;
|
||||
import com.reandroid.archive2.io.CountingOutputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
public class OutputSource {
|
||||
private final InputSource inputSource;
|
||||
private LocalFileHeader lfh;
|
||||
private EntryBuffer entryBuffer;
|
||||
|
||||
public OutputSource(InputSource inputSource){
|
||||
this.inputSource = inputSource;
|
||||
}
|
||||
public void align(ZipAligner aligner){
|
||||
aligner.align(getInputSource(), getLocalFileHeader());
|
||||
}
|
||||
public void makeBuffer(BufferFileInput input, BufferFileOutput output) throws IOException {
|
||||
EntryBuffer entryBuffer = this.entryBuffer;
|
||||
if(entryBuffer != null){
|
||||
return;
|
||||
}
|
||||
entryBuffer = makeFromEntry();
|
||||
if(entryBuffer != null){
|
||||
this.entryBuffer = entryBuffer;
|
||||
return;
|
||||
}
|
||||
this.entryBuffer = writeBuffer(input, output);
|
||||
}
|
||||
private EntryBuffer writeBuffer(BufferFileInput input, BufferFileOutput output) throws IOException {
|
||||
long offset = output.position();
|
||||
onWriteBuffer(output);
|
||||
long length = output.position() - offset;
|
||||
return new EntryBuffer(input, offset, length);
|
||||
}
|
||||
EntryBuffer makeFromEntry(){
|
||||
return null;
|
||||
}
|
||||
|
||||
public void writeApk(ApkWriter apkWriter) throws IOException{
|
||||
EntryBuffer entryBuffer = this.entryBuffer;
|
||||
FileChannel input = entryBuffer.getZipFileInput().getFileChannel();
|
||||
input.position(entryBuffer.getOffset());
|
||||
LocalFileHeader lfh = getLocalFileHeader();
|
||||
writeLFH(lfh, apkWriter);
|
||||
writeData(input, entryBuffer.getLength(), apkWriter);
|
||||
writeDD(lfh.getDataDescriptor(), apkWriter);
|
||||
}
|
||||
public void writeCEH(ApkWriter apkWriter) throws IOException{
|
||||
LocalFileHeader lfh = getLocalFileHeader();
|
||||
CentralEntryHeader ceh = CentralEntryHeader.fromLocalFileHeader(lfh);
|
||||
ceh.writeBytes(apkWriter.getOutputStream());
|
||||
}
|
||||
private void writeLFH(LocalFileHeader lfh, ApkWriter apkWriter) throws IOException{
|
||||
lfh.writeBytes(apkWriter.getOutputStream());
|
||||
}
|
||||
private void writeData(FileChannel input, long length, ApkWriter apkWriter) throws IOException{
|
||||
long offset = apkWriter.position();
|
||||
LocalFileHeader lfh = getLocalFileHeader();
|
||||
lfh.setFileOffset(offset);
|
||||
apkWriter.write(input, length);
|
||||
}
|
||||
void writeDD(DataDescriptor dataDescriptor, ApkWriter apkWriter) throws IOException{
|
||||
if(dataDescriptor == null){
|
||||
return;
|
||||
}
|
||||
dataDescriptor.writeBytes(apkWriter.getOutputStream());
|
||||
}
|
||||
void onWriteBuffer(BufferFileOutput output) throws IOException {
|
||||
LocalFileHeader lfh = getLocalFileHeader();
|
||||
|
||||
InputSource inputSource = getInputSource();
|
||||
OutputStream rawStream = output.getOutputStream();
|
||||
|
||||
CountingOutputStream<OutputStream> rawCounter = new CountingOutputStream<>(rawStream);
|
||||
CountingOutputStream<DeflaterOutputStream> deflateCounter = null;
|
||||
|
||||
if(inputSource.getMethod() != ZipEntry.STORED){
|
||||
DeflaterOutputStream deflaterInputStream =
|
||||
new DeflaterOutputStream(rawCounter, new Deflater(Deflater.BEST_SPEED, true), true);
|
||||
deflateCounter = new CountingOutputStream<>(deflaterInputStream, false);
|
||||
}
|
||||
if(deflateCounter != null){
|
||||
rawCounter.disableCrc(true);
|
||||
inputSource.write(deflateCounter);
|
||||
deflateCounter.close();
|
||||
rawCounter.close();
|
||||
}else {
|
||||
inputSource.write(rawCounter);
|
||||
}
|
||||
|
||||
lfh.setCompressedSize(rawCounter.getSize());
|
||||
|
||||
if(deflateCounter != null){
|
||||
lfh.setMethod(ZipEntry.DEFLATED);
|
||||
lfh.setCrc(deflateCounter.getCrc());
|
||||
lfh.setSize(deflateCounter.getSize());
|
||||
}else {
|
||||
lfh.setSize(rawCounter.getSize());
|
||||
lfh.setMethod(ZipEntry.STORED);
|
||||
lfh.setCrc(rawCounter.getCrc());
|
||||
}
|
||||
}
|
||||
|
||||
public InputSource getInputSource() {
|
||||
return inputSource;
|
||||
}
|
||||
public LocalFileHeader getLocalFileHeader(){
|
||||
if(lfh == null){
|
||||
lfh = createLocalFileHeader();
|
||||
clearAlignment(lfh);
|
||||
}
|
||||
return lfh;
|
||||
}
|
||||
LocalFileHeader createLocalFileHeader(){
|
||||
InputSource inputSource = getInputSource();
|
||||
LocalFileHeader lfh = new LocalFileHeader();
|
||||
lfh.setSignature(ZipSignature.LOCAL_FILE);
|
||||
lfh.getGeneralPurposeFlag().initDefault();
|
||||
lfh.setFileName(inputSource.getAlias());
|
||||
lfh.setMethod(inputSource.getMethod());
|
||||
return lfh;
|
||||
}
|
||||
private void clearAlignment(LocalFileHeader lfh){
|
||||
lfh.getGeneralPurposeFlag().setHasDataDescriptor(false);
|
||||
lfh.setDataDescriptor(null);
|
||||
lfh.setExtra(null);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.writter;
|
||||
|
||||
import com.reandroid.apk.RenamedInputSource;
|
||||
import com.reandroid.archive2.io.ArchiveEntrySource;
|
||||
|
||||
public class RenamedArchiveSource extends ArchiveOutputSource{
|
||||
public RenamedArchiveSource(RenamedInputSource<?> inputSource) {
|
||||
super(inputSource);
|
||||
}
|
||||
@Override
|
||||
ArchiveEntrySource getArchiveSource(){
|
||||
return (ArchiveEntrySource)
|
||||
((RenamedInputSource<?>)super.getInputSource()).getInputSource();
|
||||
}
|
||||
}
|
76
src/main/java/com/reandroid/archive2/writter/ZipAligner.java
Normal file
76
src/main/java/com/reandroid/archive2/writter/ZipAligner.java
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.writter;
|
||||
|
||||
import com.reandroid.archive.InputSource;
|
||||
import com.reandroid.archive2.block.DataDescriptor;
|
||||
import com.reandroid.archive2.block.LocalFileHeader;
|
||||
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
public class ZipAligner {
|
||||
private long mTotalPadding;
|
||||
private long mCurrentOffset;
|
||||
private boolean enableDataDescriptor = true;
|
||||
public ZipAligner(){
|
||||
}
|
||||
public void align(InputSource inputSource, LocalFileHeader lfh){
|
||||
int padding;
|
||||
if(inputSource.getMethod() != ZipEntry.STORED){
|
||||
padding = 0;
|
||||
createDataDescriptor(lfh);
|
||||
}else {
|
||||
int alignment = getAlignment(inputSource);
|
||||
long newOffset = mCurrentOffset + mTotalPadding;
|
||||
padding = (int) ((alignment - (newOffset % alignment)) % alignment);
|
||||
mTotalPadding += padding;
|
||||
}
|
||||
lfh.setExtra(new byte[padding]);
|
||||
mCurrentOffset += lfh.getDataSize() + lfh.countBytes();
|
||||
DataDescriptor dataDescriptor = lfh.getDataDescriptor();
|
||||
if(dataDescriptor!=null){
|
||||
mCurrentOffset += dataDescriptor.countBytes();
|
||||
}
|
||||
}
|
||||
private void createDataDescriptor(LocalFileHeader lfh){
|
||||
DataDescriptor dataDescriptor;
|
||||
if(enableDataDescriptor){
|
||||
dataDescriptor = DataDescriptor.fromLocalFile(lfh);
|
||||
}else {
|
||||
dataDescriptor = null;
|
||||
}
|
||||
lfh.setDataDescriptor(dataDescriptor);
|
||||
}
|
||||
public void reset(){
|
||||
mTotalPadding = 0;
|
||||
mCurrentOffset = 0;
|
||||
}
|
||||
public void setEnableDataDescriptor(boolean enableDataDescriptor) {
|
||||
this.enableDataDescriptor = enableDataDescriptor;
|
||||
}
|
||||
|
||||
private int getAlignment(InputSource inputSource){
|
||||
String name = inputSource.getAlias();
|
||||
if(name.startsWith("lib/") && name.endsWith(".so")){
|
||||
return ALIGNMENT_PAGE;
|
||||
}else {
|
||||
return ALIGNMENT_4;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int ALIGNMENT_4 = 4;
|
||||
private static final int ALIGNMENT_PAGE = 4096;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user