Click here to Skip to main content
Click here to Skip to main content

XZip and XUnzip - Add zip and/or unzip to your app with no extra .lib or .dll

By , 18 Jul 2007
 

Introduction

I have already introduced XZip in a previous article. This article presents XZip and also XUnzip, which together allow you to add zip and unzip to your application without using any .lib or .dll.

First, let me acknowledge the work of Lucian Wischik, who took the many .c and .h files from Info-ZIP and produced the .cpp and .h files that XZip is based on.

XZip and XUnzip Features

Most of the functions are demonstrated in the XZip demo app. Here are the main ones:

  • CreateZip() - Create a zip archive file.
//////////////////////////////////////////////////////////////////////////////
//
// CreateZip()
//
// Purpose:     Create a zip archive file
//
// Parameters:  z     - archive file name if flags is ZIP_FILENAME;  for other
//                       uses see below
//              len   - for memory (ZIP_MEMORY) should be the buffer size;
//                       for other uses, should be 0
//              flags - indicates usage, see below;  for files, this will be
//                       ZIP_FILENAME
//
// Returns:     HZIP  - non-zero if zip archive created ok, otherwise 0
//
  • ZipAdd() - Add a file to a zip archive.
//////////////////////////////////////////////////////////////////////////////
//
// ZipAdd()
//
// Purpose:     Add a file to a zip archive
//
// Parameters: hz      - handle to an open zip archive
//             dstzn   - name used inside the zip archive to identify the file
//             src     - for a file (ZIP_FILENAME) this specifies the filename
//                        to be added to the archive;  for other uses, see 
//                        below
//             len     - for memory (ZIP_MEMORY) this specifies the buffer
//                        length;  for other uses, this should be 0
//             flags   - indicates usage, see below;  for files, this will be
//                        ZIP_FILENAME
//
// Returns:    ZRESULT - ZR_OK if success, otherwise some other value
//
  • OpenZip() - Open an existing zip archive file.
//////////////////////////////////////////////////////////////////////////////
//
// OpenZip()
//
// Purpose:     Open an existing zip archive file
//
// Parameters:  z      - archive file name if flags is ZIP_FILENAME;  for 
//                        other uses see below
//              len    - for memory (ZIP_MEMORY) should be the buffer size;
//                       for other uses, should be 0
//              flags  - indicates usage, see below;  for files, this will be
//                       ZIP_FILENAME
//
// Returns:     HZIP   - non-zero if zip archive opened ok, otherwise 0
//
  • GetZipItem() - Get information about an item in an open zip archive.
//////////////////////////////////////////////////////////////////////////////
//
// GetZipItem()
//
// Purpose:     Get information about an item in an open zip archive
//
// Parameters:  hz      - handle of open zip archive
//              index   - index number (0 based) of item in zip
//              ze      - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct
//                        (if Unicode)
//
// Returns:     ZRESULT - ZR_OK if success, otherwise some other value
//
  • FindZipItem() - Find item by name and return information about it.
//////////////////////////////////////////////////////////////////////////////
//
// FindZipItem()
//
// Purpose:     Find item by name and return information about it
//
// Parameters:  hz      - handle of open zip archive
//              name    - name of file to look for inside zip archive
//              ic      - TRUE = case insensitive
//              index   - pointer to index number returned, or -1
//              ze      - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct
//                        (if Unicode)
//
// Returns:     ZRESULT - ZR_OK if success, otherwise some other value
//
  • UnzipItem() - Find item by index and unzip it.
//////////////////////////////////////////////////////////////////////////////
//
// UnzipItem()
//
// Purpose:     Find item by index and unzip it
//
// Parameters:  hz      - handle of open zip archive
//              index   - index number of file to unzip
//              dst     - target file name of unzipped file
//              len     - for memory (ZIP_MEMORY. length of buffer;
//                        otherwise 0
//              flags   - indicates usage, see below;  for files, this will be
//                        ZIP_FILENAME
//
// Returns:     ZRESULT - ZR_OK if success, otherwise some other value
//
  • CloseZip() - Close an open zip archive.
//////////////////////////////////////////////////////////////////////////////
//
// CloseZip()
//
// Purpose:     Close an open zip archive
//
// Parameters:  hz      - handle to an open zip archive
//
// Returns:     ZRESULT - ZR_OK if success, otherwise some other value
//

How To Use

To integrate XZip into your app, you first need to add following the files to your project:

  • XZip.cpp
  • XZip.h
  • XUnzip.cpp
  • XUnzip.h

If you include XZip in a project that uses precompiled headers, you must change C/C++ Precompiled Headers settings to Not using precompiled headers for XZip.cpp and XUnzip.cpp.

Next, include the header files XZip.h and XUnzip.h in appropriate project files. Now you are ready to start using XZip. There are many notes concerning usage of various functions in XZip.h and XUnzip.h. Please read all function headers for each function you wish to use.

Known Limitations

XZip and XUnzip have been tested only with files.

Demo App

The XZipTest.exe demo tests the APIs in XZip and XUnzip. Here is some of the output:

screenshot

Frequently Asked Questions

  1. Can I use XZip in non-MFC apps?
    Yes. It has been implemented to compile with any Win32 program.
  2. When I try to include XZip.cpp in my MFC project, I get the compiler error XZip.cpp(2918) : fatal error C1010: unexpected end of file while looking for precompiled header directive. How can I fix this?
    When using XZip in project that uses precompiled headers, you must change C/C++ Precompiled Headers settings to Not using precompiled headers for XZip.cpp and XUnzip.cpp. Be sure to do this for All Configurations.

screenshot


  1. When I try to build the demo app, I get the linker error LINK : fatal error LNK1104: cannot open file "mfc42u.lib" Error executing link.exe. How can I fix this?
    The default installation options of Visual C++ v6.0 don't install the Unicode libraries of MFC, so you might get an error that mfc42u.lib or mfc42ud.lib cannot be found. You can fix this either by installing the Unicode libs from the VC++ install CD, or by going to Build | Set Active Configuration and selecting one of the non-Unicode configurations.

    screenshot

    You can configure the Visual Studio toolbars to include the Select Active Configuration combobox. This allows you to see at a glance what configuration you are working with.

  2. I don't need the Zip/Unzip functions. Can I exclude XZip.cpp/XUnzip.cpp?
    Yes. You only need to include the .h/.cpp pair that you need.
  3. Can we use XZip in our (shareware/commercial) app?
    Yes, you can use XZip without charge or license fee, providing you follow the Info-ZIP restrictions as defined in XZip.cpp.
  4. Does XZip handle pipes? in-memory zipping?
    XZip has not been tested with anything other than files.
  5. Can I use XZip in a VS2005 project?
    Yes. There is a sample VS2005 project included in the download.
  6. Does XZip work on Vista?
    Yes.

Acknowledgments

Revision History

Version 1.3 - 2007 July 18

  • Fixed problem with file size that is multiple of 16384, reported by Mathias Svensson.
  • Fixed XZip to save file time in local time, suggested by Damir Valiulin.

Version 1.2 - 2007 June 30

  • Added project for VS2005.
  • Added AddFolderContent() contributed by Renaud Deysine.
  • Fixed problem with TUnzip::Open() reported by Pete Howells. Open() now returns correct success code.
  • Fixed several bugs reported by Warren Stevens.
  • Fixed a problem in unzReadCurrentFile() reported by Kochise.
  • Fixed bug in EnsureDirectory() reported by craigmj.
  • Changed ideflate() suggested by Michael B. Hansen.
  • Fixed problem with time_t reported by Ronney.
  • Fixed problems found by Boundschecker as reported by Warren Stevens.
  • Made changes to PUTSHORT and PUTBYTE macros and to TZip::write(), suggested by vielheit.

Version 1.1 - 2003 May 7

  • Initial public release

Usage

This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Hans Dietrich
Software Developer (Senior) Hans Dietrich Software
United States United States
Member
I attended St. Michael's College of the University of Toronto, with the intention of becoming a priest. A friend in the University's Computer Science Department got me interested in programming, and I have been hooked ever since.
 
Recently, I have moved to Los Angeles where I am doing consulting and development work.
 
For consulting and custom software development, please see www.hdsoft.org.






Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionEnsureDirectory leaves temp directories [modified]memberMarkus Bst13 Mar '13 - 2:24 
Hello everyone,
 
first of all: thanks for this piece of code, it is really helpful and easy to use.Thumbs Up | :thumbsup:
 
I unzipped a file containing subfolders. I noticed, that the function
void EnsureDirectory(const TCHAR *rootdir, const TCHAR *dir)
creates the subfolder structure in the current working directory as well as in the target folder. Those temporary directories are not cleaned up afterwards.
 
As a quick fix, I removed the
CreateDirectory(cd,NULL)
call.
My tests still run, everything gets extracted correctly.
Does anyone have a similar experience? Did I forget something?
 
[Edit]
My tests run, because I didn't expect the Unzip-function to create subfolders. I do this before I call Unzip, that's why it works.
However, rootdir is set to
GetCurrentDirectory(MAX_PATH,rootdir);
when opening the zipfile. It is used in
TUnzip::Unzip(...)
 
As a bugfix I removed the EnsureDirectoryMethod and create the subfolder structure before I call Unzip on an item.
 
Best regards,
Markus
 
P.S.
Environment: Windows7, VS2010 SP1

modified 14 Mar '13 - 3:49.

GeneralReally useful jobmemberzirandeai25 Feb '13 - 18:53 
really useful,many thanks.
QuestionCString with path and file name [modified]memberMike Gaskey21 Jan '13 - 7:11 
I've tried everything I can think of but I cannot get the logic to accept anything other than a lliteral as in "some file name" as input for a file to be added to a just created zip file. I have the full path and file name in a CString object and have tried several ways of specifying including (void *)&MyCString for the 3rd parameter of the ZipAdd() function but it always fails. Any idea?
 
oh well. Shortly after I posted the above I found the solution to my problem:
CString wcString = "Whatever the full path and file name happen to be";
 
Then:
wcString.GetBuffer() <-- as the 3rd paramter to ZipAdd() works.
Mike - typical white guy.
 
"Political correctness is a doctrine, fostered by a delusional, illogical minority, and rabidly promoted by an unscrupulous mainstream media, which holds forth the proposition that it is entirely possible to pick up a piece of sh*t by the clean end."
 
Thomas Mann - "Tolerance becomes a crime when applied to evil."
 
As American as: hot dogs, apple and Sarah Palin.


modified 21 Jan '13 - 13:52.

QuestionBugs found and fixed.memberluuxuanduan8 Nov '12 - 23:12 
Hi guys,

Thanks for the code. I've tried it and has found and fixed 2 bugs:
1. In BOOL AddFolderContent(HZIP hZip, TCHAR* AbsolutePath, TCHAR* DirToAdd) (it didn't work):

I've fixed as follows:
 
//Duan fixed bug
CString stPath;
stPath = AbsolutePath;
stPath += RelativePathNewFileFound;
if (ZipAdd(hZip, RelativePathNewFileFound, /*RelativePathNewFileFound*/(TCHAR*)((LPCTSTR)stPath), 0, ZIP_FILENAME) != ZR_OK)
{
return FALSE;
}
 

2. Bug for Unicode mode for adding a folder in a zip: In ZRESULT ZipAdd(HZIP hz, const TCHAR *dstzn, void *src, unsigned int len, DWORD flags)

I've fixed as follows:
 
//Duan added || flags == ZIP_FOLDER
if (flags == ZIP_FILENAME || flags == ZIP_FOLDER)
{
char szDest[MAX_PATH*2];
memset(szDest, 0, sizeof(szDest));

#ifdef _UNICODE
// need to convert Unicode dest to ANSI
int nActualChars = WideCharToMultiByte(CP_ACP, // code page
0, // performance and mapping flags
(LPCWSTR) dstzn, // wide-character string
-1, // number of chars in string
szDest, // buffer for new string
MAX_PATH*2-2, // size of buffer
NULL, // default for unmappable chars
NULL); // set when default char used
if (nActualChars == 0)
return ZR_ARGS;
#else
strcpy(szDest, dstzn);
#endif

lasterrorZ = zip->Add(szDest, src, len, flags);
}
else
{
lasterrorZ = zip->Add((char *)dstzn, src, len, flags);
}
 

Best Regards,
Duan.
SuggestionFindZipItem support Windows RelativePath [modified]memberdaviyang9 Apr '12 - 0:56 
Windows system using this code find subdirectory file didn't work
FindZipItem(hz, _T("toolbar\\bk.bmp"), true, &i, &ze)
 
change code,work well
int unzLocateFile (unzFile file, const TCHAR *szFileName, int iCaseSensitivity)
{
	unz_s* s;
	int err;
 
	uLong num_fileSaved;
	uLong pos_in_central_dirSaved;
 
	if (file==NULL)
		return UNZ_PARAMERROR;
 
    if (_tcslen(szFileName)>=UNZ_MAXFILENAMEINZIP)
        return UNZ_PARAMERROR;
 
	char szFileNameA[MAX_PATH];
 
#ifdef _UNICODE
	GetAnsiFileName(szFileName, szFileNameA, MAX_PATH-1);
#else
	strcpy(szFileNameA, szFileName);
#endif
 
	// support Windows subdirectory
	int iLen=strlen(szFileNameA);
	for (int i=0;i<iLen;i++)
	{
		if (szFileNameA[i]=='\\')
		{
			szFileNameA[i]='/';
		}
	}
 
	s=(unz_s*)file;
	if (!s->current_file_ok)
		return UNZ_END_OF_LIST_OF_FILE;
 
	num_fileSaved = s->num_file;
	pos_in_central_dirSaved = s->pos_in_central_dir;
 
	err = unzGoToFirstFile(file);
 
	while (err == UNZ_OK)
	{
		char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1];
		unzGetCurrentFileInfo(file,NULL,
								szCurrentFileName,sizeof(szCurrentFileName)-1,
								NULL,0,NULL,0);
		TRACE(_T("%s\t%s"),szCurrentFileName,szFileNameA);
		if (unzStringFileNameCompare(szCurrentFileName,szFileNameA,iCaseSensitivity)==0)
			return UNZ_OK;
		err = unzGoToNextFile(file);
	}
 
	s->num_file = num_fileSaved ;
	s->pos_in_central_dir = pos_in_central_dirSaved ;
	return err;
}


modified 9 Apr '12 - 7:33.

BugWrong file time set in decompressed file in some cases - fixmemberFrank Kobs15 Dec '11 - 0:06 
I found a small bug where the file date and time is not correctly set in unzipped file.
Some zip files do not set the date/time of their contained files at all.
So DOS date / time members are zero.
 
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);
  ze->atime=ft; ze->ctime=ft; ze->mtime=ft;
  .
  .
  .
}
 
DosDateTimeToFileTime fails if dostime and and dosdate is zero.
since ft is not initialized, some random values are inside.
 
Better:
Initialize ft with current date time.
ZRESULT TUnzip::Get(int index,ZIPENTRY *ze)
{ .
  .
  WORD dostime = (WORD)(ufi.dosDate&amp;0xFFFF);
  WORD dosdate = (WORD)((ufi.dosDate&gt;&gt;16)&amp;0xFFFF);
  FILETIME ft;
 
  SYSTEMTIME st;
  GetSystemTime(&st);  // Gets the current system time
  SystemTimeToFileTime(&st, &ft);  // Converts the current system time to file time format

  DosDateTimeToFileTime(dosdate,dostime,&amp;ft);
  ze->atime=ft; ze->ctime=ft; ze->mtime=ft;
  .
  .
  .
}
 
but anyway: Thanks for sharing the code!
 
With best regards,
 
Frank Kobs
Palette CAD GmbH
BugBug in TUnzip::Get lets UnzipItem create read-only filesmemberThomas Haase1 Dec '11 - 0:45 
UnzipItem may create wrongly read-only files.
 
I'm calling the function like this:
zr = UnzipItem(hz, index, targetname, 0, ZIP_FILENAME);
 
The problem occurs when working with zip files, that have not been created with XZip.
For example, I have created zip files with
  • Windows-7 Context menu 'Send To' 'Compressed (zipped) Folder'
  • Java
  • Total Commander 7.56a
  • 7-Zip 9.20
In all cases UnzipItem creates wrongly read-only files.
 
The problem is located in TUnzip::Get when evaluating the so called upper half of the attribute value, which is called standard unix attr in the comment.
In the line, that initializes bool uwritable, what does this comment // ***hd*** mean? Does it mean Hans Dietrich?
 
I have temporary solved this issue for my use case by commenting out the evaluation of uwritable
, but I guess this won't work, if I would handle zip files that have been created on a unix machine.
 
Please advice, any comments are welcome.
 
I've already posted this issue in 2003, the problem still exist in the latest Version 1.3.
http://www.codeproject.com/Messages/681420/Problem-in-TUnzip-Get-gt-creates-sometimes-read-on.aspx[^]
Thomas Haase

Question_stricmp / _tzset instead of #pragma warning(disable : 4996)memberThomas Haase30 Nov '11 - 23:24 
Generally I don't like to hide warnings by using a global pragma. But I know, this is my personal preference.
 
But I would like to discuss an alternative solution.
 
In the past we had solved the related issue by by using _stricmp and _tzset instead of stricmp and tzset.
Are there any arguments against this alternative solution?
Thomas Haase

QuestionUpdated version available?memberDale Fugier25 Nov '11 - 12:05 
There has been a lot of fixes and enhancements posted over the years. Is there an updated version that includes all of these fixes?
AnswerRe: Updated version available?memberThomas Haase30 Nov '11 - 22:31 
Yes! Version 1.4 would be very welcome!
Thomas Haase

BugZipAdd(..., ZIP_MEMORY) With Unicode FileName Bug And Solution [modified]memberMember 833035718 Oct '11 - 16:21 
Bug
The current version of the library when building with _UNICODE defined and using
ZipAdd(zipFile, L"HelloWorld.txt", buffer, sizeof(buffer), ZIP_MEMORY)
results in a bug that only the 1st character 'H' of the filename "HelloWorld.txt" gets stored in the zip archive because the cast to (char*)dstzn produces "H\0" as the filename rather than "HelloWorld.txt\0". Reason being the wchar_t is 2 bytes and since 'H' 0x48 is stored as 2 bytes with the 1st byte being NUL 0x00 and the 2nd byte being 'H' 0x48 and in little endian it is stored in RAM as "H\0" 0x48 0x00 hence the cast to (char*) produces a null terminated string "H\0".
 
The buggy code is in XZip.cpp ZipAdd(...)
if (flags == ZIP_FILENAME)
    {
        char szDest[MAX_PATH*2];
        memset(szDest, 0, sizeof(szDest));
 
#ifdef _UNICODE
        // need to convert Unicode dest to ANSI
        int nActualChars = WideCharToMultiByte(CP_ACP,  // code page
                                0,                      // performance and mapping flags
                                (LPCWSTR) dstzn,        // wide-character string
                                -1,                     // number of chars in string
                                szDest,                 // buffer for new string
                                MAX_PATH*2-2,           // size of buffer
                                NULL,                   // default for unmappable chars
                                NULL);                  // set when default char used
        if (nActualChars == 0)
            return ZR_ARGS;
#else
        strcpy(szDest, dstzn);
#endif
 
        lasterrorZ = zip->Add(szDest, src, len, flags);
    }
    else
    {
        lasterrorZ = zip->Add((char *)dstzn, src, len, flags);
    }
 
Solution
To fix the bug the code should be simplified to this in XZip.cpp ZipAdd(...)
 
1. Check for invalid dstzn parameter
if (hz == 0)
{
    lasterrorZ = ZR_ARGS;
    return ZR_ARGS;
}
 
if (dstzn == NULL)
{
    lasterrorZ = ZR_ARGS;
    return ZR_ARGS;
}
 
TZipHandleData *han = (TZipHandleData*)hz;
 
2. Remove the if/else to always convert the filename regardless of the value of the flags parameter
TZip *zip = han->zip;
 
    char szDest[MAX_PATH*2];
    memset(szDest, 0, sizeof(szDest));
 
#ifdef _UNICODE
    // need to convert Unicode dest to ANSI
    int nActualChars = WideCharToMultiByte(CP_ACP,  // code page
                            0,                      // performance and mapping flags
                            (LPCWSTR) dstzn,        // wide-character string
                            -1,                     // number of chars in string
                            szDest,                 // buffer for new string
                            MAX_PATH*2-2,           // size of buffer
                            NULL,                   // default for unmappable chars
                            NULL);                  // set when default char used
    if (nActualChars == 0)
        return ZR_ARGS;
#else
    strcpy(szDest, dstzn);
#endif
 
    lasterrorZ = zip->Add(szDest, src, len, flags);
 
    return lasterrorZ;


modified 19 Oct '11 - 16:35.

Questionflag ZIP_FOLDER for CreateZip not workingmemberkezhu10 Aug '11 - 13:40 
Hi there:
 
Tried to use CreateZip with ZIP_FOLDER, but always have an error returned.
 
I was trying to pack several folders with files underneath into a zip file under an assigned directory.
 
Can anyone shed a light on how to do that?
 
Thanks and regards,
 
Ke
AnswerRe: flag ZIP_FOLDER for CreateZip not working [modified]memberSteven L Christy23 Aug '11 - 13:55 
The problem might be a unicode issue. The ZipAdd function is only handling unicode properly for files and not folders. Solution:
 
ZRESULT ZipAdd(HZIP hz, const TCHAR *dstzn, void *src, unsigned int len, DWORD flags)
{ 
        ...
FROM:
        if ( flags == ZIP_FILENAME )
        {
 
TO:
	if ( dstzn )
	{
 
        ...
 
}

modified on Tuesday, August 23, 2011 8:03 PM

GeneralUnzip Larger Than 2GB zip Files (solution)memberPeter V.24 May '11 - 2:59 
I really appreciate Hans and all whom worked on this code, because it works!Thumbs Up | :thumbsup:
 
I recently noticed that opening an existing zip file reports "UNZ_BADZIPFILE" when the file size is larger than 2GB. To resolve this I made a lot of changes to my copy of the code, but they can be summarized as follows for anyone who is interested (basically the issue is the variables used are too small):
 
- change all "unsigned long" variables to "unsigned __int64" (see unz_global_info, unz_file_info).
- change the definition of "uLong" from "unsigned long" to "unsigned __int64" (I just got rid of "uLong" and replaced it).
- change several "unsigned int" variables to "unsigned __int64" (specifically LUFILE struct's members).
- replace SetFilePointer() with SetFilePointerEx(), so that you can use the unsigned __int64 values.
 
An example replacement of SetFilePointer() is this in the "lufseek()" function:
 
    if (whence==SEEK_SET)
    {
        LARGE_INTEGER lnValue;
        lnValue.QuadPart = stream->initial_offset + offset;
        ::SetFilePointerEx(stream->h, lnValue, NULL, FILE_BEGIN);
    }
The files affected by this change were XUnzip.cpp and XUnzip.h (update struct's ZIPENTRY and ZIPENTRYW).
 
The limited testing I have done shows that it works, i.e. I can now successfully retrieve a listing of entries from a zip file that is larger than 2GB.
 
Hope this helps,
- Peter.
Owner Fourth Ray Software
fourthray.com
AnswerRe: Unzip Larger Than 2GB zip Files (solution)mentorHans Dietrich24 May '11 - 3:04 
Thanks, Peter.
Best wishes,
Hans
 

[Hans Dietrich Software]

GeneralRe: Unzip Larger Than 2GB zip Files (solution)membertroichet26 Jun '12 - 17:50 
i have problem when unzip large file.
 
BOOL res = ReadFile(stream->h,ptr,toread,&red,NULL);
=> red return = 0;
 
it don't have in your solution
i don't know how to slove it,
help me...
GeneralSmall Unzip Usage Modificationmemberxpmule10 Apr '11 - 21:12 
FindZipItem will fail if the first char of const TCHAR *name is a forward slash.
 
In unzLocateFile in xunzip.cpp i added 2 lines of code to make unzipping files easier.
 
while ( *(szFileName++) == 0x2F ) {} // remove / ( single forward slash )
szFileName--;
 
Adding above will now allow calling FindZipItem with or with out a leading forward slash.
 
So for example all the following works now
 
FindZipItem( hj, "icon.png", 0, &idx, &ze );
FindZipItem( hj, "/icon.png", 0, &idx, &ze );
FindZipItem( hj, "/res/image2d/icon.png", 0, &idx, &ze );
FindZipItem( hj, "META-INF/MANIFEST.MF", 0, &idx, &ze );
FindZipItem( hj, "/META-INF/MANIFEST.MF", 0, &idx, &ze );
 
And i know people read these pages lol
Come on post some comments or even better some improvements Smile | :)
Someone smart needs to update this class to allow updating existing zips, that would be cool !
AnswerRe: Small Unzip Usage ModificationmentorHans Dietrich10 Apr '11 - 21:21 
Thanks.
 
The problem is not the desire to update the class, but the fact that the original code is such a horrible spaghetti mess that almost any change will break something else. Dead | X|
Best wishes,
Hans
 

[Hans Dietrich Software]

GeneralRe: Small Unzip Usage Modificationmemberxpmule21 Apr '11 - 17:59 
im finding that out the hard way lol
 
bear in mind this is no 2 line hello world example Smile | :)
 
i don't care what anyone else says..
its impressive and i appreciate the effort put into this code !
 
i posted again because im struggling with the unzip to memory ZR_MORE bug
its driving me nuts, i still can't really see where things go wrong.
since im aware of the issue i can work around the problem but im still
searching for a proper fix..
 
i keep running through unzReadCurrentFile with the visual studio debugger
trying to see why re calling Unzipitem again fixs the issue..
 
what i've done is this.. (notice the 2nd unzip call with len=1 to get the ZR_OK set)
	HZIP hj = OpenZip( theConfig.m_sNewAppJarPath, 0, ZIP_FILENAME );
	if ( hj )
	{
		ZIPENTRY ze;
		memset( &ze, 0, sizeof(ze) );
		int idx = -1;
 
		ZRESULT fz = FindZipItem( hj, "META-INF/MANIFEST.MF", TRUE, &idx, &ze );
		if ( fz == ZR_OK )
		{
			uBufferSize = ze.unc_size;
			pBuffer = (char*)malloc( ze.unc_size );
			ZRESULT zr = UnzipItem( hj, idx, pBuffer, uBufferSize, ZIP_MEMORY ); // unzReadCurrentFile patched !
			if ( zr == ZR_MORE ) // Should NOT get here..
			{
				Trace( WA, __FUNCTION__, "Unpack Manifest.mf ZR_MORE Bug Detected - Fixing.." );
				zr = UnzipItem( hj, idx, pBuffer, 1, ZIP_MEMORY );
			}
			if ( zr != ZR_OK )
			{
				Trace( ER, __FUNCTION__, "Unpack Manifest.mf Problem Detected - Code: %08X", zr );
				return NULL;
			}
		}
		// Find Manifest.mf in .Jar File Result
		else if ( fz == ZR_NOTFOUND )
		{
			Trace( ER, __FUNCTION__, "Manifest.mf Could Not Be Found" );
			return NULL;
		}
		else
		{
			Trace( ER, __FUNCTION__, "Find Manifest.mf Problem Detected - Code: %08X", fz ); 
			return NULL;
		}
		CloseZip( hj ); // Cleanup
	}
 
something is obviously broken in unzReadCurrentFile and there has to be a proper solution ?
and a solution that will work for ZIP_MEMORY and ZIP_FILENAME etc
GeneralBetter ver of AddFolderContent()memberxpmule1 Mar '11 - 19:44 
I made changes to the function AddFolderContent in xzip.cpp i figured i'd share.
Havn't tested this a lot yet but so far it seems ok..
 
First off i made changes because because i didn't like the way AddFolderContent worked.
 
What i did was change how it names content in the .zip
BOOL AddFolderContent( HZIP hZip, TCHAR* AbsolutePath, TCHAR* DirToAdd )
normaly DirToAdd is added into the zip file and all its contents aswell.
so what i wanted was to add the contents and directory structure to a zip
with out adding the selected DirToAdd folder into the zip.
 
so this will do the same thing except the DirAdd folder will NOT be added to the zip.
 
Also before i carry on I'd like to say thank you to all the contributors, i appreciate all the effort people !
 
so here is the complete function after my changes (note: usage remains the same)
i pulled out some logging code if anyone is wondering why i added so many brackets ?
 
BOOL AddFolderContent( HZIP hZip, TCHAR* AbsolutePath, TCHAR* DirToAdd )
{
	HANDLE hFind; // file handle
	WIN32_FIND_DATA FindFileData;
	TCHAR PathToSearchInto [MAX_PATH] = {0};
	
	//if (NULL != DirToAdd)
	//if ( strcmp( DirToAdd, "" ) != 0 )
	//{
	//	ZipAdd(hZip, DirToAdd, 0, 0, ZIP_FOLDER);
	//}
	
	// Construct the path to search into "C:\\Windows\\System32\\*"
	_tcscpy(PathToSearchInto, AbsolutePath);
	_tcscat(PathToSearchInto, _T("\\"));//
	_tcscat(PathToSearchInto, DirToAdd);
	_tcscat(PathToSearchInto, _T("\\*"));
	
	hFind = FindFirstFile(PathToSearchInto,&FindFileData); // find the first file
	if(hFind == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}
	
	bool bSearch = true;
	while(bSearch) // until we finds an entry
	{
		if(FindNextFile(hFind,&FindFileData))
		{
			// Don't care about . and ..
			//if(IsDots(FindFileData.cFileName))
			if ( (_tcscmp(FindFileData.cFileName, _T(".")) == 0) || (_tcscmp(FindFileData.cFileName, _T("..")) == 0) )
				continue;
			
			// We have found a directory
			if((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
			{
				TCHAR RelativePathNewDirFound[MAX_PATH] = {0};
				_tcscat(RelativePathNewDirFound, DirToAdd);
				_tcscat(RelativePathNewDirFound, _T("\\"));
				_tcscat(RelativePathNewDirFound, FindFileData.cFileName);
 
				// Recursive call with the new directory found
				if (AddFolderContent(hZip, AbsolutePath, RelativePathNewDirFound)== FALSE)
				{
					return FALSE ;
				}
				
			}
			// We have found a file
			else
			{
				// Add the found file to the zip file
				TCHAR RelativePathNewFileFound[MAX_PATH] = {0};
				_tcscpy(RelativePathNewFileFound, DirToAdd);
				_tcscat(RelativePathNewFileFound, _T("\\"));
				_tcscat(RelativePathNewFileFound, FindFileData.cFileName);
 
				char* ptr1 = strchr( RelativePathNewFileFound, '\\' );
				
                                // if ( strcmp( ptr1, "" ) == 0 ) { // log error here ? }
 
				char* ptr2;
				if ( ptr1[0] == 0x5C ) // kill first backslash
				{
					ptr2 = strchr( ptr1, ptr1[1] );
				}
 
				if ( ZipAdd( hZip, ptr2, RelativePathNewFileFound, 0, ZIP_FILENAME) != ZR_OK )
				{
					return FALSE;
				}
			}
			
		}//FindNextFile
		else
		{
			if(GetLastError() == ERROR_NO_MORE_FILES) // no more files there
			{
				bSearch = false;
			}
			else
			{
				// some error occured, close the handle and return FALSE
				FindClose(hFind);
				return FALSE;
			}
		}
	}//while
	
	FindClose(hFind); // closing file handle
 
	return true;
}

GeneralRe: Better ver of AddFolderContent()mentorHans Dietrich1 Mar '11 - 20:08 
This is excellent, and I appreciate your willingness to share.
 
Two questions:
 
1. Did you run the tests in the demo for this code?
 
2. I haven't had a chance to look at this in detail, but I will post your version - with credit to you - if you would like me to. Is this acceptable? If yes, please send me zip (do not include .exe) of complete project with your changes. Make whatever changes to file headers you think appropriate. Send zip to me at hdietrich at gmail dot com.
Best wishes,
Hans
 

[Hans Dietrich Software]

GeneralRe: Better ver of AddFolderContent()memberxpmule2 Mar '11 - 10:34 
Lots of respect to you Hans, i am no professional programmer
but im a 35yr old guy who likes to work on projects as a hobby
and im making a Hack Tool for an LG Cell Phone and have used the zip
code to extract & write files to .JAR files. My tool rebuilds a databse
and reformats content for the purpose of adding apps to LG cell phones.
 
Last night i did a bit of searching on the Host OS stated in the Zip header
and i changed my code to mimic the same OS & ver as reported when making a zip with winrar.
I think it may be interesting to a #define or something to the header so the coder can
select what host/version he wants in his zips

in TZip::Add i changed the following,
	zfi.vem = (ush)0x0014; //0xB17; // 0xB00 is win32 os-code. 0x17 is 23 in decimal: zip 2.3
	zfi.ver = (ush)20;
That will show in winrar info box that the archive is DOS 2.0 and not windows 2.0
 
Here is a chart i found on the net about this issue..
also im not sure it makes any difference anyway lol
 
PkZip Host OS table
0 - MS-DOS and OS/2 (FAT)
1 - Amiga
2 - VMS
3 - *nix
4 - VM/CMS
5 - Atari ST
6 - OS/2 1.2 extended file sys
7 - Macintosh
8-255 - unused
*/
 
I've made my own source mod many years ago and i noticed THIS code is still currently
beeing used by VALVE inc. in all their source based games but they are using ver 1.0 of the code
Just an interesting observation i figured most people wouldn't know.
 
I highly suggest warning people somewhere in the documentation that you can NOT
call OpenZip and the AddZip with the same handle, i find that a sad limitation in this code
in an otherwise very impressive package of code.
 
Im working on mod'ing AddFolderContent right now to return the num of items written
that way you can use the return result to validate zip creation etc
(its almost done just having probs getting folders counted)
 
Lastly i will try running your test code Hans i didnt think of that, good idea (i seen it / i know what you mean)
and if i can help out at all then ya for sure ill do what i can to share my changes..
GeneralRe: Better ver of AddFolderContent()memberxpmule2 Mar '11 - 13:42 
so this is what i've done so far to AddFolderContent
 
to use this you have to add the following line to XZIP.cpp
extern int NumFilesAdded = 0;
It will have to be added up high enough so CloseZip can use it.
 
Then add NumFilesAdded = 0; to CloseZipZ as shown below.
 
ZRESULT CloseZipZ(HZIP hz)
{ 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;
  lasterrorZ = zip->Close();
  delete zip;
  delete han;
  NumFilesAdded = 0;
  return lasterrorZ;
}
 
Then change the function to the version i pasted below.
Change return type in header from BOOL to int also.
 
/*
* Change #1. Modified return type from bool to int in order
* to return number of items added to newly created zip.
* Also added global variable NumFilesAdded to store items added.
* Usage example: CreateZip -> AddFolderContent -> CloseZip
* Then OpenZip -> GetZipItem -> CloseZip. 
* Then compare AddFolderContent return result to items reported in GetZipItem.
* If the results match then zip was created correctly.
* Change #2. Removed param #3 TCHAR* DirToAdd from beeing written to zip.
* Note: AddFolderContent Parameter Usage remains the same.
* And each call to CloseZip will set the variable NumFilesAdded back to 0.
*/
int AddFolderContent( HZIP hZip, TCHAR* AbsolutePath, TCHAR* DirToAdd )
{
	HANDLE hFind; // file handle
	WIN32_FIND_DATA FindFileData;
	TCHAR PathToSearchInto [MAX_PATH] = {0};
 
	// Construct the path to search into "C:\\Windows\\System32\\*"
	_tcscpy(PathToSearchInto, AbsolutePath);
	_tcscat(PathToSearchInto, _T("\\"));//
	_tcscat(PathToSearchInto, DirToAdd);
	_tcscat(PathToSearchInto, _T("\\*"));
 
	hFind = FindFirstFile(PathToSearchInto,&FindFileData); // find the first file
	if(hFind == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}
	
	bool bSearch = true;
	while(bSearch) // until we finds an entry
	{
		if(FindNextFile(hFind,&FindFileData))
		{
			// Don't care about . and ..
			//if(IsDots(FindFileData.cFileName))
			if ( (_tcscmp(FindFileData.cFileName, _T(".")) == 0) || (_tcscmp(FindFileData.cFileName, _T("..")) == 0) )
				continue;
			
			// We have found a directory
			if ( FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
			{
				TCHAR RelativePathNewDirFound[MAX_PATH] = {0};
				_tcscat(RelativePathNewDirFound, DirToAdd);
				_tcscat(RelativePathNewDirFound, _T("\\"));
				_tcscat(RelativePathNewDirFound, FindFileData.cFileName);
 
				// Recursive call with the new directory found
				if ( AddFolderContent(hZip, AbsolutePath, RelativePathNewDirFound) == FALSE )
				{
					return FALSE ;
				}
				
			}
			// We have found a file
			else
			{
				// Add the found file to the zip file
				TCHAR RelativePathNewFileFound[MAX_PATH] = {0};
				_tcscpy(RelativePathNewFileFound, DirToAdd);
				_tcscat(RelativePathNewFileFound, _T("\\"));
				_tcscat(RelativePathNewFileFound, FindFileData.cFileName);
 
				char* ptr1 = strchr( RelativePathNewFileFound, '\\' );
 
				//if ( strcmp( ptr1, "" ) == 0 ){}
 
				char* ptr2;
				if ( ptr1[0] == 0x5C ) // kill first backslash
				{
					ptr2 = strchr( ptr1, ptr1[1] );
				}
 
				if ( ZipAdd(hZip, ptr2, RelativePathNewFileFound, 0, ZIP_FILENAME) != ZR_OK )
				{
					return false;
				}
				else
				{
					NumFilesAdded += 1;
				}
			}
		}//FindNextFile
		else
		{
			if ( GetLastError() == ERROR_NO_MORE_FILES ) // no more files there
			{
				bSearch = false;
			}
			else
			{
				// some error occured, close the handle and return FALSE
				FindClose(hFind);
				return false;
			}
		}
	}//while
	FindClose(hFind); // closing file handle
	return NumFilesAdded;
}
 
i created a function in my program that shows how i'd create a zip from a folder
and then verify the number of items added..
 
int CDatabaseTools::CreateJar( void* Jar, char* szPath, char* szDirectory )
{
	// Create Zip & add folder contents.
	HZIP hz = CreateZip( Jar, 0, ZIP_FILENAME );
	int res = AddFolderContent( hz, szPath,  szDirectory );
	CloseZip( hz );
 
	// verify newly created zip.
	ZIPENTRY ze;
	memset( &ze, 0, sizeof(ze) );
 
	HZIP hj = OpenZip( Jar, 0, ZIP_FILENAME );
	GetZipItem( hj, -1, &ze );
	CloseZip( hj );
 
	int numitems = ze.index;
 
	if ( res != numitems )
	{
		Trace( ER, __FUNCTION__, "Zip Verification Failed" );
		return 1;
	}
	else
	{
		Trace( ZP, __FUNCTION__, "Zip Verified" );
		return 0;
	}
}
 
and finally here is how i called my new function in my mfc app..
 
void CPPgKeybo2::OnBnClickedButton2()
{
	int res = theDatabaseTools.CreateJar( "D:\\LG Mobile Tools\\Test.jar", "D:\\LG Mobile Tools",  "tmp" );
	if ( res != 0 )
	{
		Trace( ER, __FUNCTION__, "Create New .Jar Failed" );
	}
        else
        {
                Trace( ZP, __FUNCTION__, "Create New .Jar Succeeded" );
        }
}

GeneralUnzip bug fixmemberxpmule27 Feb '11 - 10:56 
TUnzip::Unzip creates read only files, i don't know if this was intended by i find it very lame !
so i made a quick fix i figured i'd share.. change the line in Unzip like below,
 
From:
h = ::CreateFile((const TCHAR*)dst, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, ze.attr, NULL);
 
To:
h = ::CreateFile((const TCHAR*)dst, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
 
Also i can't believe this is ANOTHER zip class that supports almost everything except
adding a single file to an existing zip LOL that just blows me away !
And just to confuse the hell out of everyone the extensive code comments deliberatly avoid touching on the subject,
leaving the users to post on here, why ? why can't i .. etc
The least that could have been done is add a warning in the page about it if it can't be mentioned in the code.
Sorry but that really ticked me off because i keep finding the same issue with other similar zip projects
and this was the 3rd in a row that was missing that feature AND seemed to forget to mention..
YOU CAN'T ADD FILES TO ZIP's ..unless you create one yourself first
 
normaly i staticly link to zlib but was hoping for an easier alternative..
oh.. and if i get UPDATING zip's working I will post my fix for everyone else too
GeneralRe: Unzip bug fixmemberxpmule10 Apr '11 - 11:21 
this read only fix i posted doesn't always work. it seemed to work at first but
after trying many different zip files i found in some cases you would get nothing
but an empty file with no error reported..
 
so.. couple years back someone posted that you change the create attribute to the following,
ze.attr ^= FILE_ATTRIBUTE_READONLY
now i have not tested this at all but i am concerned that if you did want a file
to be extracted as read only this other fix would make that impossible.
now i dont know very much about the zip format but i did a quick test with winrar..
i made a file read-only and zip'd it then extracted it to a new folder and
the newly created/extracted file WAS extracted out preserving the read-only attribute.
 
my point ? i think that other guys fix would break an intended feature..
 
so i did some debugging to follow along what was happening in xunzip.cpp when the attributes get set
and the problem has nothing to do with reading or setting the read only attribute (best i can tell)
The problem is actualy in the below code..
bool uwriteable= (a&0x00800000)!=0;	// ***hd***
if (!uwriteable||wreadonly) ze->attr|=FILE_ATTRIBUTE_READONLY;
uwriteable is getting set FALSE when it should be set to TRUE
 
so what i did was change the block of code that line is found at and
set the originaly modified line i posted before back to its original form as shown below..
h = ::CreateFile((const TCHAR*)dst, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, ze.attr, NULL);
 
the following code is what i changed basicly ( found in TUnzip::Get )
  unsigned long a = ufi.external_fa;
 
  bool uisdir    = ( a&0x40000000 ) != 0;
  bool wreadonly = ( a&0x00000001 ) != 0;
  bool whidden   = ( a&0x00000002 ) != 0;
  bool wsystem   = ( a&0x00000004 ) != 0;
  bool wisdir    = ( a&0x00000010 ) != 0;
  bool warchive  = ( a&0x00000020 ) != 0;
 
  ze->attr=FILE_ATTRIBUTE_NORMAL;
 
  if ( uisdir || wisdir ) ze->attr |= FILE_ATTRIBUTE_DIRECTORY;
  if ( warchive )         ze->attr |= FILE_ATTRIBUTE_ARCHIVE;
  if ( whidden )          ze->attr |= FILE_ATTRIBUTE_HIDDEN;
  if ( wreadonly )        ze->attr |= FILE_ATTRIBUTE_READONLY;
  if ( wsystem )          ze->attr |= FILE_ATTRIBUTE_SYSTEM;
 
This change (i removed the uwritable check) should hopefully solve the files beeing created read only problem
UNLESS they are SUPPOSE to be extracted as read only.
 
And im not sure why yet this is happening, if its because some files are simply
missing unix attributes or if the code is simply interpeting the found data incorrectly ?
 
someone smarter wants to fix this ? i'd look in the below call..
unzlocal_GetCurrentFileInfoInternal calls unzlocal_getLong(s->file,&file_info.external_fa)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 18 Jul 2007
Article Copyright 2003 by Hans Dietrich
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid