blob: a7867e964da3577d2689601f424bd4d87db1c50b [file] [log] [blame]
//===----------------- Zip.cpp - Interface with zlib ----------------------===//
//
// The VMKit project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <zlib.h>
#include <mvm/Allocator.h>
#include "JavaArray.h"
#include "Reader.h"
#include "Zip.h"
using namespace j3;
ZipArchive::ZipArchive(ArrayUInt8* bytes, mvm::BumpPtrAllocator& A) : allocator(A) {
this->bytes = bytes;
findOfscd();
if (ofscd > -1) addFiles();
}
ZipFile* ZipArchive::getFile(const char* filename) {
table_iterator End = filetable.end();
table_iterator I = filetable.find(filename);
return I != End ? I->second : 0;
}
#define END_CENTRAL_DIRECTORY_FILE_HEADER_SIZE 18
#define CENTRAL_DIRECTORY_FILE_HEADER_SIZE 42
#define LOCAL_FILE_HEADER_SIZE 26
#define C_FILENAME_LENGTH 24
#define C_UCSIZE 20
#define C_CSIZE 16
#define C_EXTRA_FIELD_LENGTH 26
#define C_FILE_COMMENT_LENGTH 28
#define C_ROLH 38
#define C_COMPRESSION_METHOD 6
#define L_FILENAME_LENGTH 22
#define L_EXTRA_FIELD_LENGTH 24
#define E_OFFSET_START_CENTRAL_DIRECTORY 12
#define HDR_ENDCENTRAL "PK\005\006"
#define HDR_CENTRAL "PK\001\002"
#define HDR_LOCAL "PK\003\004"
#define PATH_SEPARATOR '/'
#define ZIP_STORE 0
#define ZIP_DEFLATE 8
#define DEF_WBITS 15
static uint32 readEndianDep4(Reader& reader) {
uint8 one = reader.readU1();
uint8 two = reader.readU1();
uint8 three = reader.readU1();
uint8 four = reader.readU1();
return (one + (two << 8) + (three << 16) + (four << 24));
}
static uint16 readEndianDep2(Reader& reader) {
uint8 one = reader.readU1();
uint8 two = reader.readU1();
return (one + (two << 8));
}
void ZipArchive::findOfscd() {
sint32 curOffs = 0;
sint32 minOffs = 0;
sint32 st = END_CENTRAL_DIRECTORY_FILE_HEADER_SIZE + 4;
Reader reader(&bytes);
curOffs = reader.max;
if (curOffs >= (65535 + END_CENTRAL_DIRECTORY_FILE_HEADER_SIZE + 4)) {
minOffs = curOffs - (65535 + END_CENTRAL_DIRECTORY_FILE_HEADER_SIZE + 4);
} else {
minOffs = 0;
}
while (curOffs > minOffs) {
sint32 searchPos = 0;
if (curOffs >= (1024 - st)) {
curOffs = curOffs - (1024 - st);
} else {
curOffs = 0;
}
reader.cursor += curOffs;
sint32 diff = reader.max - reader.cursor;
sint32 temp = reader.cursor;
if (diff > 1024) {
searchPos = 1024;
reader.cursor += 1024;
} else {
searchPos = diff;
reader.cursor = reader.max;
}
if (searchPos >= st) {
sint32 searchPtr = temp + (searchPos - st);
while (searchPtr > temp) {
if ((*(reader.bytes))->elements[searchPtr] == 'P' &&
!(memcmp(&((*(reader.bytes))->elements[searchPtr]), HDR_ENDCENTRAL, 4))) {
sint32 offset = searchPtr + 4 + E_OFFSET_START_CENTRAL_DIRECTORY;
reader.cursor = offset;
this->ofscd = readEndianDep4(reader);
return;
}
}
}
}
this->ofscd = -1;
}
void ZipArchive::addFiles() {
sint32 temp = ofscd;
Reader reader(&bytes);
reader.cursor = temp;
while (true) {
if (memcmp(&((*(reader.bytes))->elements[temp]), HDR_CENTRAL, 4)) return;
ZipFile* ptr = new(allocator, "ZipFile") ZipFile();
reader.cursor = temp + 4 + C_COMPRESSION_METHOD;
ptr->compressionMethod = readEndianDep2(reader);
reader.cursor = temp + 4 + C_CSIZE;
ptr->csize = readEndianDep4(reader);
ptr->ucsize = readEndianDep4(reader);
ptr->filenameLength = readEndianDep2(reader);
ptr->extraFieldLength = readEndianDep2(reader);
ptr->fileCommentLength = readEndianDep2(reader);
reader.cursor = temp + 4 + C_ROLH;
ptr->rolh = readEndianDep4(reader);
temp = temp + 4 + CENTRAL_DIRECTORY_FILE_HEADER_SIZE;
if ((ptr->filenameLength > 1024) ||
(reader.max - temp) < ptr->filenameLength)
return;
ptr->filename = (char*)allocator.Allocate(ptr->filenameLength + 1,
"Zip file name");
memcpy(ptr->filename, &((*(reader.bytes))->elements[temp]),
ptr->filenameLength);
ptr->filename[ptr->filenameLength] = 0;
if (ptr->filename[ptr->filenameLength - 1] != PATH_SEPARATOR) {
filetable.insert(std::make_pair(ptr->filename, ptr));
}
temp = temp + ptr->filenameLength + ptr->extraFieldLength +
ptr->fileCommentLength;
}
}
sint32 ZipArchive::readFile(ArrayUInt8* array, const ZipFile* file) {
uint32 bytesLeft = 0;
uint32 filenameLength = 0;
uint32 extraFieldLength = 0;
char* ptr = (char*)array->elements;
uint32 temp = 0;
Reader reader(&bytes);
reader.cursor = file->rolh;
if (!(memcmp(&((*(reader.bytes))->elements[file->rolh]), HDR_LOCAL, 4))) {
reader.cursor += 4;
temp = reader.cursor;
reader.cursor += L_FILENAME_LENGTH;
filenameLength = readEndianDep2(reader);
extraFieldLength = readEndianDep2(reader);
reader.cursor =
temp + extraFieldLength + filenameLength + LOCAL_FILE_HEADER_SIZE;
if (file->compressionMethod == ZIP_STORE) {
memcpy(ptr, &((*(reader.bytes))->elements[reader.cursor]), file->ucsize);
return 1;
} else if (file->compressionMethod == ZIP_DEFLATE) {
z_stream stre;
sint32 err = 0;
bytesLeft = file->csize;
stre.next_out = (Bytef*)ptr;
stre.avail_out = file->ucsize;
stre.zalloc = 0;
stre.zfree = 0;
err = inflateInit2_(&stre, - DEF_WBITS, zlib_version, sizeof(z_stream));
if (err != Z_OK) {
return 0;
}
while (bytesLeft) {
uint32 size = 0;
stre.next_in = &((*(reader.bytes))->elements[reader.cursor]);
if (bytesLeft > 1024) size = 1024;
else size = bytesLeft;
uint32 diff = reader.max - reader.cursor;
if (diff < size) {
stre.avail_in = diff;
reader.cursor = reader.max;
} else {
stre.avail_in = size;
reader.cursor += size;
}
if (bytesLeft > size) {
err = inflate(&stre, Z_PARTIAL_FLUSH);
} else {
err = inflate(&stre, Z_FINISH);
}
bytesLeft = bytesLeft - size;
}
inflateEnd(&stre);
if ((err != Z_STREAM_END) &&
(bytesLeft || err != Z_BUF_ERROR || stre.avail_out)) {
return 0;
} else {
return 1;
}
} else {
return 0;
}
} else {
return 0;
}
return 0;
}