|
I recently reached the same situation as you . I also took a quick look at the code and I finally figured out a way to use it without modifying anything in the unzip code.
I do call UnzipItem() with the proper buffer size, according to the uncompress size obtained with GetZipItem(). The file gets uncompressed at that time but ZR_MORE error code is returned. I then call again UnzipItem with 1 byte buffer. It returns OK at that time, confirming that there was nothing else to unzip for that file.
I tried it with buffers of various lengths and various contents and I got successful results . Included is a part of the code used to perform the tests.
Hoping its gonna help others...
TiL_MtL
#define BUFLENBIG 40000
void Tst(char* pBuf, int len)
{
ZRESULT lZErr;
void* lZipBuf;
unsigned long lZipLen;
ZIPENTRY lZEntry;
char lZipFile[BUFLENBIG];
char lUnzipFile[BUFLENBIG];
char lDummy;
HZIP lZH = CreateZip(lZipFile, BUFLENBIG, ZIP_MEMORY);
ASSERT(lZH != NULL);
lZErr = ZipAdd(lZH, "1", pBuf, len, ZIP_MEMORY);
ASSERT(lZErr == ZR_OK);
lZErr = ZipGetMemory(lZH, &lZipBuf, &lZipLen);
ASSERT(lZErr == ZR_OK);
ASSERT(lZipBuf == lZipFile);
lZErr = CloseZip(lZH);
ASSERT(lZErr == ZR_OK);
HZIP lZH2 = OpenZip(lZipBuf, lZipLen, ZIP_MEMORY);
ASSERT(lZH2 != NULL);
lZErr = GetZipItem(lZH2, 0, &lZEntry);
ASSERT(lZErr == ZR_OK);
ASSERT(lZEntry.unc_size == len);
lZErr = UnzipItem(lZH2, 0, lUnzipFile, lZEntry.unc_size, ZIP_MEMORY);
ASSERT(lZErr == ZR_MORE);
lZErr = UnzipItem(lZH2, 0, &lDummy, 1, ZIP_MEMORY);
ASSERT(lZErr == ZR_OK);
lZErr = CloseZip(lZH2);
ASSERT(lZErr == ZR_OK);
ASSERT(memcmp(lUnzipFile, pBuf, len) == 0);
}
|
|
|
|
|
Hello,
first of all...this code is a great thing, but i wonder how to use all the stuff written on Unix too.
At the moment i develop a small project with QtCreator on windows, but the source is completely windows independant and can be compiled on Unix without any code changes. And maybe some of the ppl here had same problems and created a source package with all needed changes?
Otherwise i bite the bullet and work out a solution by myself (why to reinvent the wheel ^^)
so long
Sebastian
|
|
|
|
|
Hi, is there a way to show a progress to the user when a lengthy compression operation is happening? May be a callback function?
|
|
|
|
|
Yes, this has been suggested before. I will try to add it,
|
|
|
|
|
It seems current version has bug for filetime setting,
if files are zipped by XZip, everything goes well,
'cause XZip save CTime ,ATime and MTime in its extra data field.
but some other zip tools do not,
they just use last mod file time/date in file header,
when XZip unzip files zipped by other tools,
filetimes will be wrong,especially for MTime,
I think it's a most important time for a file.
Following codes may clear this,
I have tested only on NTFS,FAT not yet,please confirm it.
ZRESULT TUnzip::Get(int index,ZIPENTRY *ze)
WORD dostime = (WORD)(ufi.dosDate&0xFFFF);
WORD dosdate = (WORD)((ufi.dosDate>>16)&0xFFFF);
FILETIME ft;
DosDateTimeToFileTime(dosdate,dostime,&ft);
FILETIME utcft;
LocalFileTimeToFileTime(&ft,&utcft);
ze->atime=utcft; ze->ctime=utcft; ze->mtime=utcft;
|
|
|
|
|
Hello,
Do you know that library almost works on Windows mobile. Parts that need to be altered are: setting file time and reading / writing to files. On WM calls like:
CreateFile((const TCHAR*)dst, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
ze.attr, NULL);
can cause trouble especially when unziping to storage cards. They can successfully be replaced with std calls. For example I replaced that one with std::ofstream .
For file time I have no suggestions.
BTW, thanks for sharing this.
Velja Radenkovic
www.lusys.net
|
|
|
|
|
Hi Hans,
Thanks for posting this sample - looks like it could be useful.
You may (or may not) already know of this, but when you compile an x64 build (with VC++ 2005), you get the following error:
crtdefs.h(543) : fatal error C1189: #error : You cannot use 32-bit time_t (_USE_32BIT_TIME_T) with _WIN64
I have not tried to diagnose this - just though you might want know.
Thanks,
- Dale
|
|
|
|
|
This is what I did to get XUnzip.cpp to compile under Visual Studio 2010:
1. Comment out the #define _USE_32BIT_TIME_T
2. Cast to __time32_t where appropriate (wherever there's a 4-byte time value)
if (hasmtime)
{ time_t mtime = *(__time32_t*)(extra+epos); epos+=4;
ze->mtime = timet2filetime(mtime);
}
if (hasatime)
{ time_t atime = *(__time32_t*)(extra+epos); epos+=4;
ze->atime = timet2filetime(atime);
}
if (hasctime)
{ time_t ctime = *(__time32_t*)(extra+epos);
ze->ctime = timet2filetime(ctime);
}
Haven't tested this thoroughly so use at your own risk!
Eric
|
|
|
|
|
I ran into the same problem. This ist my solution:
1.) Skip #define _USE_32BIT_TIME_T for 64-Bit builds:
#ifndef _WIN64
#define _USE_32BIT_TIME_T //+++1.2
#endif
2.) Change time_t and time functions to the explicit 32-Bit versions:
time_t ---> __time32_t
mktime(&tm) ---> _mktime32(&tm)
gmtime(&timer) ---> _gmtime32(&timer);
This seems to work fine. But I haven't test it thoroughly yet.
|
|
|
|
|
I have tested this solution too, and it seems to work well.
Anyway, I would prefer an 'official' solution in 'Version 1.4'.
Thomas Haase
|
|
|
|
|
would be nice - but not work with filesize over 4gb ...
sascha
|
|
|
|
|
Great work .
But there's a bug in the UNICODE build:
ZRESULT ZipAdd(HZIP hz, const TCHAR *dstzn, void *src, unsigned int len, DWORD flags)
does the WideCharToMultiByte() conversion only for flags == ZIP_FILENAME . So it fails in AddFolderContent() where it is called with flags==ZIP_FOLDER .
I think it should be like this:
ZRESULT ZipAdd(HZIP hz, const TCHAR *dstzn, void *src, unsigned int len, DWORD flags)
{
if (hz == 0)
{
lasterrorZ = ZR_ARGS;
return ZR_ARGS;
}
TZipHandleData *han = (TZipHandleData*)hz;
if (han->flag != 2)
{
lasterrorZ = ZR_ZMODE;
return ZR_ZMODE;
}
TZip *zip = han->zip;
char szDest[MAX_PATH*2];
memset(szDest, 0, sizeof(szDest));
#ifdef _UNICODE
int nActualChars = WideCharToMultiByte(CP_ACP,
0,
(LPCWSTR) dstzn,
-1,
szDest,
MAX_PATH*2-2,
NULL,
NULL);
if (nActualChars == 0)
return ZR_ARGS;
#else
strcpy(szDest, dstzn);
#endif
if (flags == ZIP_FILENAME)
lasterrorZ = zip->Add(szDest, src, len, flags);
else
lasterrorZ = zip->Add(szDest, src, len, flags);
return lasterrorZ;
}
|
|
|
|
|
Hello,
how can i show progress while creating the zip-file?
cause biger files (100mb) take long time to zip...
thanks!
|
|
|
|
|
Hi,
I am trying to use the following code to open a .zip file. The file exists at the path indicated. Can anyone see what is wrong here?
#ifdef _UNICODE
ZIPENTRYW ze;
#else
ZIPENTRY ze;
#endif
memset(&ze, 0, sizeof(ze));
TCHAR * pszZipPath = _T("C:\\Test.zip");
TCHAR * pszZipBook = _T("Test.txt");
int iZipFileIndex = -1;
ZRESULT zrZipResult = 0;
HZIP hz = OpenZip(&pszZipPath,0, ZIP_FILENAME);
if (hz)
{
}
Many thanks in advance.
|
|
|
|
|
Oops... Found it. The OpenZip line should have been:
HZIP hz = OpenZip(pszZipPath, 0, ZIP_FILENAME);
It works great now.
|
|
|
|
|
AddFolderContent does not add any file to ZIP if the zip location is not the current folder
...and here is the fix in XZip.cpp replace this
if (ZipAdd(hZip, RelativePathNewFileFound, RelativePathNewFileFound, 0, ZIP_FILENAME) != ZR_OK)
{
return FALSE;
}
with this
TCHAR tcFullPath[MAX_PATH]={0};
_tcscpy(tcFullPath, AbsolutePath);
_tcscat(tcFullPath, _T("\\"));
_tcscat(tcFullPath, RelativePathNewFileFound);
if (ZipAdd(hZip, RelativePathNewFileFound, tcFullPath, 0, ZIP_FILENAME) != ZR_OK)
{
return FALSE;
}
modified on Friday, October 17, 2008 7:07 AM
|
|
|
|
|
Specifically when working over a typical Windows network... If there is a .zip with 1000+ little files and I retrieve one file from it, will this use only the bandwith to retrieve the one file (plus a little for overhead if necessary)? Or, does it need to pull the whole file down to work with it?
Thanks in advance.
|
|
|
|
|
Hi, Hans,
when i use ZIP_FILENAME i can retrieve the size of the result (whatever compressing or decompressing) by retrieving the filesize after CloseZIP. But when using the mode ZIP_MEMORY how can i retrieve the whole size of the compressed (or uncompressed) data which are stored in the buffer??
e.g. when i supply the call of CreateZip with a pointer to a buffer with 100 KB and now i want to compressed data of 80 KB how can i retrieve the size of the ZIP archiv in the buffer (total, with headers, entries ...) ? The same question for decompressing ...
Thanx for a hint!
Greetings from germany!
Frank
|
|
|
|
|
... by accident i found the method
ZRESULT ZipGetMemory(HZIP hz, void **buf, unsigned long *len);
which is the solution for me.
@Hans: Because i think this is an important method when using this class in memory mode you should add this method explicit to the documentation in the article ...
Greetings from germany!
Frank
|
|
|
|
|
First of all - great job!
Can I use this class to zip/unzip with passwords (thus encrypting the data) ???
Thanks!!!
|
|
|
|
|
Hans,
First, thank you for a very useful class.
I am trying to use it for a large archive (10k+ files, binary and text), and so far the results are fine. However, I'm encountering an error with XUnzip. I'm using the following code:
#ifdef _UNICODE
ZIPENTRYW ze;
#else
ZIPENTRY ze;
#endif
memset(&ze, 0, sizeof(ze));
int index = -1;
ZRESULT zr = 0;
zr = FindZipItem(hz, itemId, FALSE, &index, &ze);
if (!(*buf) || (bufLen > ze.unc_size)) {
if (*buf) delete[] *buf;
bufLen = ze.unc_size + 1;
*buf = new char[bufLen];
}
zr = UnzipItem(hz, index, *buf, bufLen, ZIP_MEMORY);
if (zr != ZR_OK) {
TRACE(_T("UnzipItem failed for %s\r\n"), itemId);
}
UnzipItem always fails. I dug a bit into your code, and found this line to be the problem (XUnzip.cpp, line 3676):
if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead;
For some reason, even though the whole stream was read and deflated, unzReadCurrentFile was returning non-zero value, which is being interpretted as an incomplete operation by Unzip(). My solution (hack?) was to change the iRead==0 condition to be iRead < len (since len may be larger than iRead, for example when the buffer is larger than the stream you are trying to read). I might be overseing something here, so would love to hear your comment on this.
Also, as indicated by someone else on this thread, XUnzip can be painfully slow with large ZIP files (10k+ files). Other tools like Total Commander and WinRAR seem to unzip much faster than XUnzip does. I've found the culprit to be in unzLocateFile (called by FindZipItem) - it seem to go through all files in the archive and compare their name with the requested file. I bet there's a better approach available with the ZIP format? also, this could be optimized by loading the files table into a memory-based map, or at least support faster searching for files that are in a zip with alphabettic sorted files. Would like to hear your thoughts on this one as well.
Cheers,
Stilgar.
|
|
|
|
|
Hi Stilgar,
Your first comment about the line
if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; is correct. What you say makes sense, but unfortunately doesn't work. I changed it, then had to change it back (notice the 1.3). If you want to experiment, please feel free - but be sure to run the validation tests on any changes you make.
The history of XZip is told in the file header. It has been modified, and then modified some more, and I would not try to make any major changes unless I was prepared to do a complete rewrite. I think some of the initial design integrity has been lost in all the changes.
If you are willing to make the effort, I think it is possible to make significant improvement. I hope you will consider this, since you seem to be able to analyze the code in depth.
|
|
|
|
|
It worked for my use case, that is all I needed...
Unfortunately I cannot afford spending any more time on this, this being a very small part in the project I'm working on... I've found another solution at CP that wraps zlib, and has a much faster seeking. It still lacks several things I need, but the basic seem very solid.
Also, I think using a zlib wrapper is much safer than having a custom written class that may be inconsistent with the ZIP format - probably exactly what is happening with your code.
Stilgar.
|
|
|
|
|
I assume that a zip file contain a bounch of individually zipped blobs representing the individually files.
Would it not be possible to open an existing zip file and "re-assemble" it excluding some of the files, to have the functionality of removing a file from an archive.
I am using this zip-function to add application day logs and replacing each log every 30 day. But to replace a log, I create a temporary new zip file, add 29 of the old days logs and finaly the new days log. There must be a way to do this without having to unzip and rezip each file?
Thanks and regards
Karl
|
|
|
|
|
Hi!
The code's great, and works really well for zipping - thanks, Hans.
I'm now trying to add a text comment to associate with the zip file. How can I do this? I tried adding a string value to the comment variable of a TZipFileInfo object in the TZip::Add() method, but it does not seem to work:
zfi.comment="abcd\0";
zfi.com=5;
I also tried 'abcd', length = 4.
Any ideas?
Thanks,
Kanika
|
|
|
|
|