#include "zip.h" namespace Upp { void UnZip::ReadDir() { error = true; file.Clear(); current = 0; int64 entries = -1; int64 offset; int64 zipsize = zip->GetSize(); int64 pos = zipsize - 1; //22; zip->Seek(max((int64)0, zip->GetSize() - 4000)); // Precache end of zip zip->Get(); int64 zip64eocdl = 0; while(pos >= max((int64)0, zip->GetSize() - 65536)) { zip->ClearError(); zip->Seek(pos); entries = -1; // ensure error return when header fails if(zip->Get32le() == 0x06054b50) { zip->Get16le(); // number of this disk zip->Get16le(); // number of the disk with the start of the central directory int h = (word)zip->Get16le(); // total number of entries in the central directory on this disk entries = (word)zip->Get16le(); // total number of entries in the central directory if(h != entries) // Multiple disks not supported return; zip->Get32le(); // size of the central directory offset = (dword)zip->Get32le(); //offset of start of central directory with respect to the starting disk number zip64eocdl = pos - 20; // offset of zip64 end of central directory locator int commentlen = zip->Get16le(); if(zip->GetPos() + commentlen == zipsize) break; } pos--; } if(entries < 0) return; zip->Seek(zip64eocdl); if(zip->Get32le() == 0x07064b50) { zip->Get32le(); // ZIP64 end of central directory locator : number of the disk with the start of the zip64 end of central directory int64 zip64eocdr = zip->Get64le(); // ZIP64 end of central directory locator : relative offset of the zip64 end of central directory record zip->Seek(zip64eocdr); if(zip->Get32le() == 0x06064b50) { zip->Get64le(); // ZIP64 end of central directory record : the rest of the record after this field zip->Get16le(); // ZIP64 end of central directory record : version made by zip->Get16le(); // ZIP64 end of central directory record : version required to extract zip->Get32le(); // ZIP64 end of central directory record : disk number zip->Get32le(); // ZIP64 end of central directory record : number of disk with the start of central directory int64 de = zip->Get64le(); // ZIP64 end of central directory record : number of directory entries on this disk int64 te = zip->Get64le(); // ZIP64 end of central directory record : number of directory entries (total) if(de != te) // Multiple disks not supported return; if(entries == 0xffff) entries = te; // Use ZIP64 entry counter zip->Get64le(); // size of the central directory if(offset == 0xffffffff) offset = zip->Get64le(); // ZIP64 offset of the central directory } } zip->Seek(offset); for(int i = 0; i < entries; i++) { if(zip->Get32le() != 0x02014b50 && zip->IsEof()) return; File& f = file.Add(); zip->Get16le(); zip->Get16le(); f.bit = zip->Get16le(); // general purpose bit flag f.method = zip->Get16le(); f.time = zip->Get32le(); f.crc = zip->Get32le(); f.csize = (dword)zip->Get32le(); f.usize = (dword)zip->Get32le(); int fnlen = zip->Get16le(); int extralen = zip->Get16le(); // extra field length 2 bytes int commentlen = zip->Get16le(); // file comment length 2 bytes if(zip->Get16le() != 0) // disk number start 2 bytes return; // Multiple disks not supported zip->Get16le(); // internal file attributes zip->Get32le(); // external file attributes f.offset = zip->Get32le(); f.path = zip->Get(fnlen); int64 skipto = zip->GetPos() + extralen + commentlen; if(extralen>=4 && zip->Get16le()==1){ // ZIP64 extra field : header ID int bytes = zip->Get16le(); // ZIP64 extra field : bytes to follow if(bytes>=8) f.usize = zip->Get64le(); // ZIP64 extra field : uncomp size if(bytes>=16) f.csize = zip->Get64le(); // ZIP64 extra field : comp size if(bytes>=24) f.offset = zip->Get64le(); // ZIP64 extra field : relative offset of local header } zip->Seek(skipto); if(zip->IsEof() || zip->IsError()) return; } error = false; } Time UnZip::GetZipTime(dword dt) { Time time; time.year = int16(((dt >> 25) & 0x7f) + 1980); time.month = byte((dt >> 21) & 0x0f); time.day = byte((dt >> 16) & 0x1f); time.hour = byte((dt >> 11) & 0x1f); time.minute = byte((dt >> 5) & 0x3f); time.second = byte((dt << 1) & 0x3e); return time; } int64 zPress(Stream& out, Stream& in, int64 size, Gate progress, bool gzip, bool compress, dword *crc, bool hdr); bool UnZip::ReadFile(Stream& out, Gate progress) { if(error) return false; if(IsFolder()) { current++; return true; } error = true; if(current >= file.GetCount()) return false; const File& f = file[current]; zip->Seek(f.offset); if(zip->Get32le() != 0x04034b50) return false; zip->Get16le(); zip->Get16le(); // Skip header, use info from centrall dir zip->Get16le(); zip->Get32le(); zip->Get32le(); zip->Get32le(); zip->Get32le(); dword filelen = zip->Get16le(); dword extralen = zip->Get16le(); zip->SeekCur(filelen + extralen); dword crc; qword l; if(f.method == 0) { Buffer temp(65536); int loaded; int64 count = f.csize; Crc32Stream crc32; while(count > 0 && (loaded = zip->Get(temp, (int)min(count, 65536))) > 0) { out.Put(temp, loaded); crc32.Put(temp, loaded); count -= loaded; } if(count > 0) return false; l = f.csize; crc = crc32; } else if(f.method == 8) l = zPress(out, *zip, f.csize, AsGate64(progress), false, false, &crc, false); else return false; if(crc != f.crc || l != f.usize) return false; current++; error = false; return true; } String UnZip::ReadFile(Gate progress) { StringStream ss; return ReadFile(ss, progress) ? ss.GetResult() : String::GetVoid(); } String UnZip::ReadFile(const char *path, Gate progress) { for(int i = 0; i < file.GetCount(); i++) if(file[i].path == path) { Seek(i); return ReadFile(progress); } return String::GetVoid(); } void UnZip::Create(Stream& _zip) { zip = &_zip; ReadDir(); } UnZip::UnZip(Stream& in) { Create(in); } UnZip::UnZip() { error = true; zip = NULL; } UnZip::~UnZip() {} }