package com.android.vending.expansion.zipfile;
/*
* Copyright (C) 2012 The Android Open Source Project
*
* 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.
*/
import android.content.res.AssetFileDescriptor;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.EOFException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Collection;
import java.util.HashMap;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class ZipResourceFile {
//
// Read-only access to Zip archives, with minimal heap allocation.
//
static final String LOG_TAG = "zipro";
static final boolean LOGV = false;
// 4-byte number
static private int swapEndian(int i)
{
return ((i & 0xff) << 24) + ((i & 0xff00) << 8) + ((i & 0xff0000) >>> 8)
+ ((i >>> 24) & 0xff);
}
// 2-byte number
static private int swapEndian(short i)
{
return ((i & 0x00FF) << 8 | (i & 0xFF00) >>> 8);
}
/*
* Zip file constants.
*/
static final int kEOCDSignature = 0x06054b50;
static final int kEOCDLen = 22;
static final int kEOCDNumEntries = 8; // offset to #of entries in file
static final int kEOCDSize = 12; // size of the central directory
static final int kEOCDFileOffset = 16; // offset to central directory
static final int kMaxCommentLen = 65535; // longest possible in ushort
static final int kMaxEOCDSearch = (kMaxCommentLen + kEOCDLen);
static final int kLFHSignature = 0x04034b50;
static final int kLFHLen = 30; // excluding variable-len fields
static final int kLFHNameLen = 26; // offset to filename length
static final int kLFHExtraLen = 28; // offset to extra length
static final int kCDESignature = 0x02014b50;
static final int kCDELen = 46; // excluding variable-len fields
static final int kCDEMethod = 10; // offset to compression method
static final int kCDEModWhen = 12; // offset to modification timestamp
static final int kCDECRC = 16; // offset to entry CRC
static final int kCDECompLen = 20; // offset to compressed length
static final int kCDEUncompLen = 24; // offset to uncompressed length
static final int kCDENameLen = 28; // offset to filename length
static final int kCDEExtraLen = 30; // offset to extra length
static final int kCDECommentLen = 32; // offset to comment length
static final int kCDELocalOffset = 42; // offset to local hdr
static final int kCompressStored = 0; // no compression
static final int kCompressDeflated = 8; // standard deflate
/*
* The values we return for ZipEntryRO use 0 as an invalid value, so we want
* to adjust the hash table index by a fixed amount. Using a large value
* helps insure that people don't mix & match arguments, e.g. to
* findEntryByIndex().
*/
static final int kZipEntryAdj = 10000;
static public final class ZipEntryRO {
public ZipEntryRO(final String zipFileName, final File file, final String fileName) {
mFileName = fileName;
mZipFileName = zipFileName;
mFile = file;
}
public final File mFile;
public final String mFileName;
public final String mZipFileName;
public long mLocalHdrOffset; // offset of local file header
/* useful stuff from the directory entry */
public int mMethod;
public long mWhenModified;
public long mCRC32;
public long mCompressedLength;
public long mUncompressedLength;
public long mOffset = -1;
public void setOffsetFromFile(RandomAccessFile f, ByteBuffer buf) throws IOException {
long localHdrOffset = mLocalHdrOffset;
try {
f.seek(localHdrOffset);
f.readFully(buf.array());
if (buf.getInt(0) != kLFHSignature) {
Log.w(LOG_TAG, "didn't find signature at start of lfh");
throw new IOException();
}
int nameLen = buf.getShort(kLFHNameLen) & 0xFFFF;
int extraLen = buf.getShort(kLFHExtraLen) & 0xFFFF;
mOffset = localHdrOffset + kLFHLen + nameLen + extraLen;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
/**
* Calculates the offset of the start of the Zip file entry within the
* Zip file.
*
* @return the offset, in bytes from the start of the file of the entry
*/
public long getOffset() {
return mOffset;
}
/**
* isUncompressed
*
* @return true if the file is stored in uncompressed form
*/
public boolean isUncompressed() {
return mMethod == kCompressStored;
}
public AssetFileDescriptor getAssetFileDescriptor() {
if (mMethod == kCompressStored) {
ParcelFileDescriptor pfd;
try {
pfd = ParcelFileDescriptor.open(mFile, ParcelFileDescriptor.MODE_READ_ONLY);
return new AssetFileDescriptor(pfd, getOffset(), mUncompressedLength);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
public String getZipFileName() {
return mZipFileName;
}
public File getZipFile() {
return mFile;
}
}
private HashMap<String, ZipEntryRO> mHashMap = new HashMap<String, ZipEntryRO>();
/* for reading compressed files */
public HashMap<File, ZipFile> mZipFiles = new HashMap<File, ZipFile>();
public ZipResourceFile(String zipFileName) throws IOException {
addPatchFile(zipFileName);
}
ZipEntryRO[] getEntriesAt(String path) {
Vector<ZipEntryRO> zev = new Vector<ZipEntryRO>();
Collection<ZipEntryRO> values = mHashMap.values();
if (null == path)
path = "";
int length = path.length();
for (ZipEntryRO ze : values) {
if (ze.mFileName.startsWith(path)) {
if (-1 == ze.mFileName.indexOf('/', length)) {
zev.add(ze);
}
}
}
ZipEntryRO[] entries = new ZipEntryRO[zev.size()];
return zev.toArray(entries);
}
public ZipEntryRO[] getAllEntries() {
Collection<ZipEntryRO> values = mHashMap.values();
return values.toArray(new ZipEntryRO[values.size()]);
}
/**
* getAssetFileDescriptor allows for ZipResourceFile to directly feed
* Android API's that want an fd, offset, and length such as the
* MediaPlayer. It also allows for the class to be used in a content
* provider that can feed video players. The file must be stored
* (non-compressed) in the Zip file for this to work.
*
* @param assetPath
* @return the asset file descriptor for the file, or null if the file isn't
* pres