| //===----------------- 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 "vmkit/Allocator.h" |
| |
| #include "JavaArray.h" |
| #include "Reader.h" |
| #include "Zip.h" |
| |
| using namespace j3; |
| |
| ZipArchive::ZipArchive(ClassBytes* bytes, vmkit::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 (bytes->elements[searchPtr] == 'P' && |
| !(memcmp(bytes->elements + searchPtr, HDR_ENDCENTRAL, 4))) { |
| sint32 offset = searchPtr + 4 + E_OFFSET_START_CENTRAL_DIRECTORY; |
| reader.cursor = offset; |
| this->ofscd = readEndianDep4(reader); |
| return; |
| } |
| searchPtr--; |
| } |
| } |
| } |
| this->ofscd = -1; |
| } |
| |
| void ZipArchive::addFiles() { |
| sint32 temp = ofscd; |
| |
| Reader reader(bytes); |
| reader.cursor = temp; |
| |
| while (true) { |
| if (memcmp(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, 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(ClassBytes* array, const ZipFile* file) { |
| uint32 bytesLeft = 0; |
| uint32 filenameLength = 0; |
| uint32 extraFieldLength = 0; |
| uint32 temp = 0; |
| |
| Reader reader(bytes); |
| reader.cursor = file->rolh; |
| |
| if (!(memcmp(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(array->elements, 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*)array->elements; |
| 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 = 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; |
| } |