
com.reandroid.archive.Archive Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ARSCLib Show documentation
Show all versions of ARSCLib Show documentation
Android binary resources read/write library
/*
* 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.archive;
import com.reandroid.apk.APKLogger;
import com.reandroid.archive.block.*;
import com.reandroid.archive.io.*;
import com.reandroid.archive.model.CentralFileDirectory;
import com.reandroid.archive.model.LocalFileDirectory;
import com.reandroid.utils.collection.ArrayIterator;
import com.reandroid.utils.collection.FilterIterator;
import com.reandroid.utils.io.FileUtil;
import com.reandroid.utils.io.IOUtil;
import java.io.*;
import java.util.*;
import java.util.function.Predicate;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
public abstract class Archive implements Closeable {
private final T zipInput;
private final ArchiveEntry[] entryList;
private final EndRecord endRecord;
private final ApkSignatureBlock apkSignatureBlock;
public Archive(T zipInput) throws IOException {
this.zipInput = zipInput;
CentralFileDirectory cfd = new CentralFileDirectory();
cfd.visit(zipInput);
this.endRecord = cfd.getEndRecord();
LocalFileDirectory lfd = new LocalFileDirectory(cfd);
lfd.visit(zipInput);
this.entryList = lfd.buildArchiveEntryList();
this.apkSignatureBlock = lfd.getApkSigBlock();
}
public ZipEntryMap createZipEntryMap(){
return new ZipEntryMap(mapEntrySource());
}
public InputSource[] getInputSources(){
ArchiveEntry[] entryList = this.entryList;
int length = entryList.length;
InputSource[] sources = new InputSource[length];
int dirCount = 0;
for(int i = 0; i < length; i++){
ArchiveEntry entry = entryList[i];
if(entry.isDirectory()){
//TODO: make InputSource for directory
dirCount++;
continue;
}
InputSource inputSource = createInputSource(entry);
inputSource.setSort(i);
sources[i] = inputSource;
}
if(dirCount == 0){
return sources;
}
InputSource[] filtered = new InputSource[length - dirCount];
int index = 0;
for(int i = 0; i < length; i++){
InputSource inputSource = sources[i];
if(inputSource == null){
continue;
}
filtered[index] = inputSource;
index++;
}
return filtered;
}
public LinkedHashMap mapEntrySource(){
ArchiveEntry[] entryList = this.entryList;
int length = entryList.length;
LinkedHashMap map = new LinkedHashMap<>(length);
for(int i = 0; i < length; i++){
ArchiveEntry entry = entryList[i];
if(entry.isDirectory()){
continue;
}
InputSource inputSource = createInputSource(entry);
inputSource.setSort(i);
map.put(inputSource.getAlias(), inputSource);
}
return map;
}
public T getZipInput() {
return zipInput;
}
abstract InputSource createInputSource(ArchiveEntry entry);
public InputSource getEntrySource(String path){
if(path == null){
return null;
}
ArchiveEntry[] entryList = this.entryList;
int length = entryList.length;
for(int i = 0; i < length; i++){
ArchiveEntry entry = entryList[i];
if(entry.isDirectory()){
continue;
}
if(path.equals(entry.getName())){
return createInputSource(entry);
}
}
return null;
}
public InputStream openRawInputStream(ArchiveEntry archiveEntry) throws IOException {
return zipInput.getInputStream(archiveEntry.getFileOffset(), archiveEntry.getDataSize());
}
public InputStream openInputStream(ArchiveEntry archiveEntry) throws IOException {
InputStream rawInputStream = openRawInputStream(archiveEntry);
if(!archiveEntry.isCompressed()){
return rawInputStream;
}
return new InflaterInputStream(rawInputStream,
new Inflater(true), 1024*1000);
}
public Iterator iterator() {
return new ArrayIterator<>(entryList);
}
public ApkSignatureBlock getApkSignatureBlock() {
return apkSignatureBlock;
}
public EndRecord getEndRecord() {
return endRecord;
}
public int extractAll(File dir) throws IOException {
return extractAll(dir, null, null);
}
public int extractAll(File dir, APKLogger logger) throws IOException {
return extractAll(dir, null, logger);
}
public int extractAll(File dir, Predicate filter) throws IOException {
return extractAll(dir, filter, null);
}
public int extractAll(File dir, Predicate filter, APKLogger logger) throws IOException {
FilterIterator iterator =
new FilterIterator(this.iterator(), filter){
@Override
public boolean test(ArchiveEntry archiveEntry){
return archiveEntry != null && !archiveEntry.isDirectory();
}
};
int result = 0;
while (iterator.hasNext()){
ArchiveEntry archiveEntry = iterator.next();
extract(toFile(dir, archiveEntry), archiveEntry, logger);
result ++;
}
return result;
}
public void extract(File file, ArchiveEntry archiveEntry) throws IOException{
extract(file, archiveEntry, null);
}
public void extract(File file, ArchiveEntry archiveEntry, APKLogger logger) throws IOException{
File parent = file.getParentFile();
if(parent != null && !parent.exists()){
parent.mkdirs();
}
if(logger != null){
long size = archiveEntry.getDataSize();
if(size > LOG_LARGE_FILE_SIZE){
logger.logVerbose("Extracting ["
+ FileUtil.toReadableFileSize(size) + "] "+ archiveEntry.getName());
}
}
if(archiveEntry.getMethod() != Archive.STORED){
extractCompressed(file, archiveEntry);
}else {
extractStored(file, archiveEntry);
}
}
abstract void extractStored(File file, ArchiveEntry archiveEntry) throws IOException;
private void extractCompressed(File file, ArchiveEntry archiveEntry) throws IOException {
FileOutputStream outputStream = new FileOutputStream(file);
IOUtil.writeAll(openInputStream(archiveEntry), outputStream);
outputStream.close();
}
private File toFile(File dir, ArchiveEntry archiveEntry){
String name = archiveEntry.getName().replace('/', File.separatorChar);
return new File(dir, name);
}
@Override
public void close() throws IOException {
this.zipInput.close();
}
private static final long LOG_LARGE_FILE_SIZE = 1024 * 1000 * 20;
public static final int STORED = 0;
public static final int DEFLATED = 8;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy