mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 22:02:49 -06:00
208 lines
6.2 KiB
C++
208 lines
6.2 KiB
C++
#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<int64, int64> progress, bool gzip,
|
|
bool compress, dword *crc, bool hdr);
|
|
|
|
bool UnZip::ReadFile(Stream& out, Gate<int, int> 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<byte> temp(65536);
|
|
int loaded;
|
|
int64 count = f.csize;
|
|
Crc32Stream crc32;
|
|
while(count > 0 && (loaded = zip->Get(temp, (int)min<int64>(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<int, int> progress)
|
|
{
|
|
StringStream ss;
|
|
return ReadFile(ss, progress) ? ss.GetResult() : String::GetVoid();
|
|
}
|
|
|
|
String UnZip::ReadFile(const char *path, Gate<int, int> 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() {}
|
|
|
|
}
|