/* zip.c -- IO on .zip files using zlib
   Version 1.1, February 14h, 2010
   part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )

         Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )

         Modifications for Zip64 support
         Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )

         For more info read MiniZip_info.txt

         Changes
   Oct-2009 - Mathias Svensson - Remove old C style function prototypes
   Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives
   Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions.
   Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data
                                 It is used when recreting zip archive with RAW when deleting items from a zip.
                                 ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed.
   Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required)
   Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../zlib/zlib.h"
#include "zip.h"

#ifdef STDC
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#endif
#ifdef NO_ERRNO_H
extern int errno;
#else
#include <errno.h>
#endif

#ifndef local
#define local static
#endif
/* compile with -Dlocal if your debugger can't find static symbols */

#ifndef VERSIONMADEBY
#define VERSIONMADEBY (0x0) /* platform depedent */
#endif

#ifndef Z_BUFSIZE
#define Z_BUFSIZE (64 * 1024)  //(16384)
#endif

#ifndef Z_MAXFILENAMEINZIP
#define Z_MAXFILENAMEINZIP (256)
#endif

#ifndef ALLOC
#define ALLOC(size) (malloc(size))
#endif
#ifndef TRYFREE
#define TRYFREE(p)      \
	{                   \
		if (p) free(p); \
	}
#endif

/*
#define SIZECENTRALDIRITEM (0x2e)
#define SIZEZIPLOCALHEADER (0x1e)
*/

/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */

// NOT sure that this work on ALL platform
#define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32))

#ifndef SEEK_CUR
#define SEEK_CUR 1
#endif

#ifndef SEEK_END
#define SEEK_END 2
#endif

#ifndef SEEK_SET
#define SEEK_SET 0
#endif

#ifndef DEF_MEM_LEVEL
#if MAX_MEM_LEVEL >= 8
#define DEF_MEM_LEVEL 8
#else
#define DEF_MEM_LEVEL MAX_MEM_LEVEL
#endif
#endif
const char zip_copyright[] = " zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";

#define SIZEDATA_INDATABLOCK (4096 - (4 * 4))

#define LOCALHEADERMAGIC (0x04034b50)
#define CENTRALHEADERMAGIC (0x02014b50)
#define ENDHEADERMAGIC (0x06054b50)
#define ZIP64ENDHEADERMAGIC (0x6064b50)
#define ZIP64ENDLOCHEADERMAGIC (0x7064b50)

#define FLAG_LOCALHEADER_OFFSET (0x06)
#define CRC_LOCALHEADER_OFFSET (0x0e)

#define SIZECENTRALHEADER (0x2e) /* 46 */

typedef struct linkedlist_datablock_internal_s
{
	struct linkedlist_datablock_internal_s* next_datablock;
	uLong avail_in_this_block;
	uLong filled_in_this_block;
	uLong unused; /* for future use and alignement */
	unsigned char data[SIZEDATA_INDATABLOCK];
} linkedlist_datablock_internal;

typedef struct linkedlist_data_s
{
	linkedlist_datablock_internal* first_block;
	linkedlist_datablock_internal* last_block;
} linkedlist_data;

typedef struct
{
	z_stream stream; /* zLib stream structure for inflate */
#ifdef HAVE_BZIP2
	bz_stream bstream; /* bzLib stream structure for bziped */
#endif

	int stream_initialised;    /* 1 is stream is initialised */
	uInt pos_in_buffered_data; /* last written byte in buffered_data */

	ZPOS64_T pos_local_header; /* offset of the local header of the file
                                     currenty writing */
	char* central_header;      /* central header data for the current file */
	uLong size_centralExtra;
	uLong size_centralheader;    /* size of the central header for cur file */
	uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */
	uLong flag;                  /* flag of the file currently writing */

	int method;                    /* compression method of file currenty wr.*/
	int raw;                       /* 1 for directly writing raw data */
	Byte buffered_data[Z_BUFSIZE]; /* buffer contain compressed data to be writ*/
	uLong dosDate;
	uLong crc32;
	int encrypt;
	int zip64; /* Add ZIP64 extened information in the extra field */
	ZPOS64_T pos_zip64extrainfo;
	ZPOS64_T totalCompressedData;
	ZPOS64_T totalUncompressedData;
#ifndef NOCRYPT
	unsigned long keys[3]; /* keys defining the pseudo-random sequence */
	const unsigned long* pcrc_32_tab;
	int crypt_header_size;
#endif
} curfile64_info;

typedef struct
{
	zlib_filefunc64_32_def z_filefunc;
	voidpf filestream;           /* io structore of the zipfile */
	linkedlist_data central_dir; /* datablock with central dir in construction*/
	int in_opened_file_inzip;    /* 1 if a file in the zip is currently writ.*/
	curfile64_info ci;           /* info on the file curretly writing */

	ZPOS64_T begin_pos; /* position of the beginning of the zipfile */
	ZPOS64_T add_position_when_writting_offset;
	ZPOS64_T number_entry;

#ifndef NO_ADDFILEINEXISTINGZIP
	char* globalcomment;
#endif

} zip64_internal;

#ifndef NOCRYPT
#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED
#include "crypt.h"
#endif

local linkedlist_datablock_internal* allocate_new_datablock()
{
	linkedlist_datablock_internal* ldi;
	ldi = (linkedlist_datablock_internal*)
		ALLOC(sizeof(linkedlist_datablock_internal));
	if (ldi != NULL)
	{
		ldi->next_datablock = NULL;
		ldi->filled_in_this_block = 0;
		ldi->avail_in_this_block = SIZEDATA_INDATABLOCK;
	}
	return ldi;
}

local void free_datablock(linkedlist_datablock_internal* ldi)
{
	while (ldi != NULL)
	{
		linkedlist_datablock_internal* ldinext = ldi->next_datablock;
		TRYFREE(ldi);
		ldi = ldinext;
	}
}

local void init_linkedlist(linkedlist_data* ll)
{
	ll->first_block = ll->last_block = NULL;
}

local void free_linkedlist(linkedlist_data* ll)
{
	free_datablock(ll->first_block);
	ll->first_block = ll->last_block = NULL;
}

local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len)
{
	linkedlist_datablock_internal* ldi;
	const unsigned char* from_copy;

	if (ll == NULL)
		return ZIP_INTERNALERROR;

	if (ll->last_block == NULL)
	{
		ll->first_block = ll->last_block = allocate_new_datablock();
		if (ll->first_block == NULL)
			return ZIP_INTERNALERROR;
	}

	ldi = ll->last_block;
	from_copy = (unsigned char*)buf;

	while (len > 0)
	{
		uInt copy_this;
		uInt i;
		unsigned char* to_copy;

		if (ldi->avail_in_this_block == 0)
		{
			ldi->next_datablock = allocate_new_datablock();
			if (ldi->next_datablock == NULL)
				return ZIP_INTERNALERROR;
			ldi = ldi->next_datablock;
			ll->last_block = ldi;
		}

		if (ldi->avail_in_this_block < len)
			copy_this = (uInt)ldi->avail_in_this_block;
		else
			copy_this = (uInt)len;

		to_copy = &(ldi->data[ldi->filled_in_this_block]);

		for (i = 0; i < copy_this; i++)
			*(to_copy + i) = *(from_copy + i);

		ldi->filled_in_this_block += copy_this;
		ldi->avail_in_this_block -= copy_this;
		from_copy += copy_this;
		len -= copy_this;
	}
	return ZIP_OK;
}

/****************************************************************************/

#ifndef NO_ADDFILEINEXISTINGZIP
/* ===========================================================================
   Inputs a long in LSB order to the given file
   nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T)
*/

local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte));
local int zip64local_putValue(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)
{
	unsigned char buf[8];
	int n;
	for (n = 0; n < nbByte; n++)
	{
		buf[n] = (unsigned char)(x & 0xff);
		x >>= 8;
	}
	if (x != 0)
	{ /* data overflow - hack for ZIP64 (X Roche) */
		for (n = 0; n < nbByte; n++)
		{
			buf[n] = 0xff;
		}
	}

	if (ZWRITE64(*pzlib_filefunc_def, filestream, buf, nbByte) != (uLong)nbByte)
		return ZIP_ERRNO;
	else
		return ZIP_OK;
}

local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte));
local void zip64local_putValue_inmemory(void* dest, ZPOS64_T x, int nbByte)
{
	unsigned char* buf = (unsigned char*)dest;
	int n;
	for (n = 0; n < nbByte; n++)
	{
		buf[n] = (unsigned char)(x & 0xff);
		x >>= 8;
	}

	if (x != 0)
	{ /* data overflow - hack for ZIP64 */
		for (n = 0; n < nbByte; n++)
		{
			buf[n] = 0xff;
		}
	}
}

/****************************************************************************/

local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm)
{
	uLong year = (uLong)ptm->tm_year;
	if (year >= 1980)
		year -= 1980;
	else if (year >= 80)
		year -= 80;
	return (uLong)(((ptm->tm_mday) + (32 * (ptm->tm_mon + 1)) + (512 * year)) << 16) |
		   ((ptm->tm_sec / 2) + (32 * ptm->tm_min) + (2048 * (uLong)ptm->tm_hour));
}

/****************************************************************************/

local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int* pi));

local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int* pi)
{
	unsigned char c;
	int err = (int)ZREAD64(*pzlib_filefunc_def, filestream, &c, 1);
	if (err == 1)
	{
		*pi = (int)c;
		return ZIP_OK;
	}
	else
	{
		if (ZERROR64(*pzlib_filefunc_def, filestream))
			return ZIP_ERRNO;
		else
			return ZIP_EOF;
	}
}

/* ===========================================================================
   Reads a long in LSB order from the given gz_stream. Sets
*/
local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX));

local int zip64local_getShort(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX)
{
	uLong x;
	int i = 0;
	int err;

	err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
	x = (uLong)i;

	if (err == ZIP_OK)
		err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
	x += ((uLong)i) << 8;

	if (err == ZIP_OK)
		*pX = x;
	else
		*pX = 0;
	return err;
}

local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX));

local int zip64local_getLong(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX)
{
	uLong x;
	int i = 0;
	int err;

	err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
	x = (uLong)i;

	if (err == ZIP_OK)
		err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
	x += ((uLong)i) << 8;

	if (err == ZIP_OK)
		err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
	x += ((uLong)i) << 16;

	if (err == ZIP_OK)
		err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
	x += ((uLong)i) << 24;

	if (err == ZIP_OK)
		*pX = x;
	else
		*pX = 0;
	return err;
}

local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T* pX));

local int zip64local_getLong64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T* pX)
{
	ZPOS64_T x;
	int i = 0;
	int err;

	err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
	x = (ZPOS64_T)i;

	if (err == ZIP_OK)
		err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
	x += ((ZPOS64_T)i) << 8;

	if (err == ZIP_OK)
		err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
	x += ((ZPOS64_T)i) << 16;

	if (err == ZIP_OK)
		err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
	x += ((ZPOS64_T)i) << 24;

	if (err == ZIP_OK)
		err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
	x += ((ZPOS64_T)i) << 32;

	if (err == ZIP_OK)
		err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
	x += ((ZPOS64_T)i) << 40;

	if (err == ZIP_OK)
		err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
	x += ((ZPOS64_T)i) << 48;

	if (err == ZIP_OK)
		err = zip64local_getByte(pzlib_filefunc_def, filestream, &i);
	x += ((ZPOS64_T)i) << 56;

	if (err == ZIP_OK)
		*pX = x;
	else
		*pX = 0;

	return err;
}

#ifndef BUFREADCOMMENT
#define BUFREADCOMMENT (0x400)
#endif
/*
  Locate the Central directory of a zipfile (at the end, just before
    the global comment)
*/
local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream));

local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)
{
	unsigned char* buf;
	ZPOS64_T uSizeFile;
	ZPOS64_T uBackRead;
	ZPOS64_T uMaxBack = 0xffff; /* maximum size of global comment */
	ZPOS64_T uPosFound = 0;

	if (ZSEEK64(*pzlib_filefunc_def, filestream, 0, ZLIB_FILEFUNC_SEEK_END) != 0)
		return 0;

	uSizeFile = ZTELL64(*pzlib_filefunc_def, filestream);

	if (uMaxBack > uSizeFile)
		uMaxBack = uSizeFile;

	buf = (unsigned char*)ALLOC(BUFREADCOMMENT + 4);
	if (buf == NULL)
		return 0;

	uBackRead = 4;
	while (uBackRead < uMaxBack)
	{
		uLong uReadSize;
		ZPOS64_T uReadPos;
		int i;
		if (uBackRead + BUFREADCOMMENT > uMaxBack)
			uBackRead = uMaxBack;
		else
			uBackRead += BUFREADCOMMENT;
		uReadPos = uSizeFile - uBackRead;

		uReadSize = ((BUFREADCOMMENT + 4) < (uSizeFile - uReadPos)) ? (BUFREADCOMMENT + 4) : (uLong)(uSizeFile - uReadPos);
		if (ZSEEK64(*pzlib_filefunc_def, filestream, uReadPos, ZLIB_FILEFUNC_SEEK_SET) != 0)
			break;

		if (ZREAD64(*pzlib_filefunc_def, filestream, buf, uReadSize) != uReadSize)
			break;

		for (i = (int)uReadSize - 3; (i--) > 0;)
			if (((*(buf + i)) == 0x50) && ((*(buf + i + 1)) == 0x4b) &&
				((*(buf + i + 2)) == 0x05) && ((*(buf + i + 3)) == 0x06))
			{
				uPosFound = uReadPos + i;
				break;
			}

		if (uPosFound != 0)
			break;
	}
	TRYFREE(buf);
	return uPosFound;
}

/*
Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before
the global comment)
*/
local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream));

local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)
{
	unsigned char* buf;
	ZPOS64_T uSizeFile;
	ZPOS64_T uBackRead;
	ZPOS64_T uMaxBack = 0xffff; /* maximum size of global comment */
	ZPOS64_T uPosFound = 0;
	uLong uL;
	ZPOS64_T relativeOffset;

	if (ZSEEK64(*pzlib_filefunc_def, filestream, 0, ZLIB_FILEFUNC_SEEK_END) != 0)
		return 0;

	uSizeFile = ZTELL64(*pzlib_filefunc_def, filestream);

	if (uMaxBack > uSizeFile)
		uMaxBack = uSizeFile;

	buf = (unsigned char*)ALLOC(BUFREADCOMMENT + 4);
	if (buf == NULL)
		return 0;

	uBackRead = 4;
	while (uBackRead < uMaxBack)
	{
		uLong uReadSize;
		ZPOS64_T uReadPos;
		int i;
		if (uBackRead + BUFREADCOMMENT > uMaxBack)
			uBackRead = uMaxBack;
		else
			uBackRead += BUFREADCOMMENT;
		uReadPos = uSizeFile - uBackRead;

		uReadSize = ((BUFREADCOMMENT + 4) < (uSizeFile - uReadPos)) ? (BUFREADCOMMENT + 4) : (uLong)(uSizeFile - uReadPos);
		if (ZSEEK64(*pzlib_filefunc_def, filestream, uReadPos, ZLIB_FILEFUNC_SEEK_SET) != 0)
			break;

		if (ZREAD64(*pzlib_filefunc_def, filestream, buf, uReadSize) != uReadSize)
			break;

		for (i = (int)uReadSize - 3; (i--) > 0;)
		{
			// Signature "0x07064b50" Zip64 end of central directory locater
			if (((*(buf + i)) == 0x50) && ((*(buf + i + 1)) == 0x4b) && ((*(buf + i + 2)) == 0x06) && ((*(buf + i + 3)) == 0x07))
			{
				uPosFound = uReadPos + i;
				break;
			}
		}

		if (uPosFound != 0)
			break;
	}

	TRYFREE(buf);
	if (uPosFound == 0)
		return 0;

	/* Zip64 end of central directory locator */
	if (ZSEEK64(*pzlib_filefunc_def, filestream, uPosFound, ZLIB_FILEFUNC_SEEK_SET) != 0)
		return 0;

	/* the signature, already checked */
	if (zip64local_getLong(pzlib_filefunc_def, filestream, &uL) != ZIP_OK)
		return 0;

	/* number of the disk with the start of the zip64 end of  central directory */
	if (zip64local_getLong(pzlib_filefunc_def, filestream, &uL) != ZIP_OK)
		return 0;
	if (uL != 0)
		return 0;

	/* relative offset of the zip64 end of central directory record */
	if (zip64local_getLong64(pzlib_filefunc_def, filestream, &relativeOffset) != ZIP_OK)
		return 0;

	/* total number of disks */
	if (zip64local_getLong(pzlib_filefunc_def, filestream, &uL) != ZIP_OK)
		return 0;
	if (uL != 1)
		return 0;

	/* Goto Zip64 end of central directory record */
	if (ZSEEK64(*pzlib_filefunc_def, filestream, relativeOffset, ZLIB_FILEFUNC_SEEK_SET) != 0)
		return 0;

	/* the signature */
	if (zip64local_getLong(pzlib_filefunc_def, filestream, &uL) != ZIP_OK)
		return 0;

	if (uL != 0x06064b50)  // signature of 'Zip64 end of central directory'
		return 0;

	return relativeOffset;
}

int LoadCentralDirectoryRecord(zip64_internal* pziinit)
{
	int err = ZIP_OK;
	ZPOS64_T byte_before_the_zipfile; /* byte before the zipfile, (>0 for sfx)*/

	ZPOS64_T size_central_dir;   /* size of the central directory  */
	ZPOS64_T offset_central_dir; /* offset of start of central directory */
	ZPOS64_T central_pos;
	uLong uL;

	uLong number_disk;         /* number of the current dist, used for
                              spaning ZIP, unsupported, always 0*/
	uLong number_disk_with_CD; /* number the the disk with central dir, used
                              for spaning ZIP, unsupported, always 0*/
	ZPOS64_T number_entry;
	ZPOS64_T number_entry_CD; /* total number of entries in
                                the central dir
                                (same than number_entry on nospan) */
	uLong VersionMadeBy;
	uLong VersionNeeded;
	uLong size_comment;

	int hasZIP64Record = 0;

	// check first if we find a ZIP64 record
	central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc, pziinit->filestream);
	if (central_pos > 0)
	{
		hasZIP64Record = 1;
	}
	else if (central_pos == 0)
	{
		central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc, pziinit->filestream);
	}

	/* disable to allow appending to empty ZIP archive
        if (central_pos==0)
            err=ZIP_ERRNO;
*/

	if (hasZIP64Record)
	{
		ZPOS64_T sizeEndOfCentralDirectory;
		if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0)
			err = ZIP_ERRNO;

		/* the signature, already checked */
		if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL) != ZIP_OK)
			err = ZIP_ERRNO;

		/* size of zip64 end of central directory record */
		if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory) != ZIP_OK)
			err = ZIP_ERRNO;

		/* version made by */
		if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy) != ZIP_OK)
			err = ZIP_ERRNO;

		/* version needed to extract */
		if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded) != ZIP_OK)
			err = ZIP_ERRNO;

		/* number of this disk */
		if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &number_disk) != ZIP_OK)
			err = ZIP_ERRNO;

		/* number of the disk with the start of the central directory */
		if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &number_disk_with_CD) != ZIP_OK)
			err = ZIP_ERRNO;

		/* total number of entries in the central directory on this disk */
		if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry) != ZIP_OK)
			err = ZIP_ERRNO;

		/* total number of entries in the central directory */
		if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry_CD) != ZIP_OK)
			err = ZIP_ERRNO;

		if ((number_entry_CD != number_entry) || (number_disk_with_CD != 0) || (number_disk != 0))
			err = ZIP_BADZIPFILE;

		/* size of the central directory */
		if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &size_central_dir) != ZIP_OK)
			err = ZIP_ERRNO;

		/* offset of start of central directory with respect to the
    starting disk number */
		if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &offset_central_dir) != ZIP_OK)
			err = ZIP_ERRNO;

		// TODO..
		// read the comment from the standard central header.
		size_comment = 0;
	}
	else
	{
		// Read End of central Directory info
		if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0)
			err = ZIP_ERRNO;

		/* the signature, already checked */
		if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL) != ZIP_OK)
			err = ZIP_ERRNO;

		/* number of this disk */
		if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &number_disk) != ZIP_OK)
			err = ZIP_ERRNO;

		/* number of the disk with the start of the central directory */
		if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &number_disk_with_CD) != ZIP_OK)
			err = ZIP_ERRNO;

		/* total number of entries in the central dir on this disk */
		number_entry = 0;
		if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL) != ZIP_OK)
			err = ZIP_ERRNO;
		else
			number_entry = uL;

		/* total number of entries in the central dir */
		number_entry_CD = 0;
		if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL) != ZIP_OK)
			err = ZIP_ERRNO;
		else
			number_entry_CD = uL;

		if ((number_entry_CD != number_entry) || (number_disk_with_CD != 0) || (number_disk != 0))
			err = ZIP_BADZIPFILE;

		/* size of the central directory */
		size_central_dir = 0;
		if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL) != ZIP_OK)
			err = ZIP_ERRNO;
		else
			size_central_dir = uL;

		/* offset of start of central directory with respect to the starting disk number */
		offset_central_dir = 0;
		if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL) != ZIP_OK)
			err = ZIP_ERRNO;
		else
			offset_central_dir = uL;

		/* zipfile global comment length */
		if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment) != ZIP_OK)
			err = ZIP_ERRNO;
	}

	if ((central_pos < offset_central_dir + size_central_dir) &&
		(err == ZIP_OK))
		err = ZIP_BADZIPFILE;

	if (err != ZIP_OK)
	{
		ZCLOSE64(pziinit->z_filefunc, pziinit->filestream);
		return ZIP_ERRNO;
	}

	if (size_comment > 0)
	{
		pziinit->globalcomment = (char*)ALLOC(size_comment + 1);
		if (pziinit->globalcomment)
		{
			size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment, size_comment);
			pziinit->globalcomment[size_comment] = 0;
		}
	}

	byte_before_the_zipfile = central_pos - (offset_central_dir + size_central_dir);
	pziinit->add_position_when_writting_offset = byte_before_the_zipfile;

	{
		ZPOS64_T size_central_dir_to_read = size_central_dir;
		size_t buf_size = SIZEDATA_INDATABLOCK;
		void* buf_read = (void*)ALLOC(buf_size);
		if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0)
			err = ZIP_ERRNO;

		while ((size_central_dir_to_read > 0) && (err == ZIP_OK))
		{
			ZPOS64_T read_this = SIZEDATA_INDATABLOCK;
			if (read_this > size_central_dir_to_read)
				read_this = size_central_dir_to_read;

			if (ZREAD64(pziinit->z_filefunc, pziinit->filestream, buf_read, (uLong)read_this) != read_this)
				err = ZIP_ERRNO;

			if (err == ZIP_OK)
				err = add_data_in_datablock(&pziinit->central_dir, buf_read, (uLong)read_this);

			size_central_dir_to_read -= read_this;
		}
		TRYFREE(buf_read);
	}
	pziinit->begin_pos = byte_before_the_zipfile;
	pziinit->number_entry = number_entry_CD;

	if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0)
		err = ZIP_ERRNO;

	return err;
}

#endif /* !NO_ADDFILEINEXISTINGZIP*/

/************************************************************/
extern zipFile ZEXPORT zipOpen3(const void* pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def)
{
	zip64_internal ziinit;
	zip64_internal* zi;
	int err = ZIP_OK;

	ziinit.z_filefunc.zseek32_file = NULL;
	ziinit.z_filefunc.ztell32_file = NULL;
	if (pzlib_filefunc64_32_def == NULL)
		fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64);
	else
		ziinit.z_filefunc = *pzlib_filefunc64_32_def;

	ziinit.filestream = ZOPEN64(ziinit.z_filefunc,
								pathname,
								(append == APPEND_STATUS_CREATE) ? (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING));

	if (ziinit.filestream == NULL)
		return NULL;

	if (append == APPEND_STATUS_CREATEAFTER)
		ZSEEK64(ziinit.z_filefunc, ziinit.filestream, 0, SEEK_END);

	ziinit.begin_pos = ZTELL64(ziinit.z_filefunc, ziinit.filestream);
	ziinit.in_opened_file_inzip = 0;
	ziinit.ci.stream_initialised = 0;
	ziinit.number_entry = 0;
	ziinit.add_position_when_writting_offset = 0;
	init_linkedlist(&(ziinit.central_dir));

	zi = (zip64_internal*)ALLOC(sizeof(zip64_internal));
	if (zi == NULL)
	{
		ZCLOSE64(ziinit.z_filefunc, ziinit.filestream);
		return NULL;
	}

	/* now we add file in a zipfile */
#ifndef NO_ADDFILEINEXISTINGZIP
	ziinit.globalcomment = NULL;
	if (append == APPEND_STATUS_ADDINZIP)
	{
		// Read and Cache Central Directory Records
		err = LoadCentralDirectoryRecord(&ziinit);
	}

	if (globalcomment)
	{
		*globalcomment = ziinit.globalcomment;
	}
#endif /* !NO_ADDFILEINEXISTINGZIP*/

	if (err != ZIP_OK)
	{
#ifndef NO_ADDFILEINEXISTINGZIP
		TRYFREE(ziinit.globalcomment);
#endif /* !NO_ADDFILEINEXISTINGZIP*/
		TRYFREE(zi);
		return NULL;
	}
	else
	{
		*zi = ziinit;
		return (zipFile)zi;
	}
}

extern zipFile ZEXPORT zipOpen2(const char* pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def)
{
	if (pzlib_filefunc32_def != NULL)
	{
		zlib_filefunc64_32_def zlib_filefunc64_32_def_fill;
		fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill, pzlib_filefunc32_def);
		return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill);
	}
	else
		return zipOpen3(pathname, append, globalcomment, NULL);
}

extern zipFile ZEXPORT zipOpen2_64(const void* pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def)
{
	if (pzlib_filefunc_def != NULL)
	{
		zlib_filefunc64_32_def zlib_filefunc64_32_def_fill;
		zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def;
		zlib_filefunc64_32_def_fill.ztell32_file = NULL;
		zlib_filefunc64_32_def_fill.zseek32_file = NULL;
		return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill);
	}
	else
		return zipOpen3(pathname, append, globalcomment, NULL);
}

extern zipFile ZEXPORT zipOpen(const char* pathname, int append)
{
	return zipOpen3((const void*)pathname, append, NULL, NULL);
}

extern zipFile ZEXPORT zipOpen64(const void* pathname, int append)
{
	return zipOpen3(pathname, append, NULL, NULL);
}

int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local)
{
	/* write the local header */
	int err;
	uInt size_filename = (uInt)strlen(filename);
	uInt size_extrafield = size_extrafield_local;

	err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)LOCALHEADERMAGIC, 4);

	if (err == ZIP_OK)
	{
		if (zi->ci.zip64)
			err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)45, 2); /* version needed to extract */
		else
			err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)20, 2); /* version needed to extract */
	}

	if (err == ZIP_OK)
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)zi->ci.flag, 2);

	if (err == ZIP_OK)
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)zi->ci.method, 2);

	if (err == ZIP_OK)
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)zi->ci.dosDate, 4);

	// CRC / Compressed size / Uncompressed size will be filled in later and rewritten later
	if (err == ZIP_OK)
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)0, 4); /* crc 32, unknown */
	if (err == ZIP_OK)
	{
		if (zi->ci.zip64)
			err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)0xFFFFFFFF, 4); /* compressed size, unknown */
		else
			err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)0, 4); /* compressed size, unknown */
	}
	if (err == ZIP_OK)
	{
		if (zi->ci.zip64)
			err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)0xFFFFFFFF, 4); /* uncompressed size, unknown */
		else
			err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)0, 4); /* uncompressed size, unknown */
	}

	if (err == ZIP_OK)
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)size_filename, 2);

	if (zi->ci.zip64)
	{
		size_extrafield += 20;
	}

	if (err == ZIP_OK)
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)size_extrafield, 2);

	if ((err == ZIP_OK) && (size_filename > 0))
	{
		if (ZWRITE64(zi->z_filefunc, zi->filestream, filename, size_filename) != size_filename)
			err = ZIP_ERRNO;
	}

	if ((err == ZIP_OK) && (size_extrafield_local > 0))
	{
		if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local)
			err = ZIP_ERRNO;
	}

	if ((err == ZIP_OK) && (zi->ci.zip64))
	{
		// write the Zip64 extended info
		short HeaderID = 1;
		short DataSize = 16;
		ZPOS64_T CompressedSize = 0;
		ZPOS64_T UncompressedSize = 0;

		// Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file)
		zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc, zi->filestream);

		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID, 2);
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize, 2);

		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize, 8);
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize, 8);
	}

	return err;
}

/*
 NOTE.
 When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped
 before calling this function it can be done with zipRemoveExtraInfoBlock

 It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize
 unnecessary allocations.
 */
extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, const char* filename, const zip_fileinfo* zipfi,
										   const void* extrafield_local, uInt size_extrafield_local,
										   const void* extrafield_global, uInt size_extrafield_global,
										   const char* comment, int method, int level, int raw,
										   int windowBits, int memLevel, int strategy,
										   const char* password, uLong crcForCrypting,
										   uLong versionMadeBy, uLong flagBase, int zip64)
{
	zip64_internal* zi;
	uInt size_filename;
	uInt size_comment;
	uInt i;
	int err = ZIP_OK;

#ifdef NOCRYPT
	if (password != NULL)
		return ZIP_PARAMERROR;
#endif

	if (file == NULL)
		return ZIP_PARAMERROR;

#ifdef HAVE_BZIP2
	if ((method != 0) && (method != Z_DEFLATED) && (method != Z_BZIP2ED))
		return ZIP_PARAMERROR;
#else
	if ((method != 0) && (method != Z_DEFLATED))
		return ZIP_PARAMERROR;
#endif

	zi = (zip64_internal*)file;

	if (zi->in_opened_file_inzip == 1)
	{
		err = zipCloseFileInZip(file);
		if (err != ZIP_OK)
			return err;
	}

	if (filename == NULL)
		filename = "-";

	if (comment == NULL)
		size_comment = 0;
	else
		size_comment = (uInt)strlen(comment);

	size_filename = (uInt)strlen(filename);

	if (zipfi == NULL)
		zi->ci.dosDate = 0;
	else
	{
		if (zipfi->dosDate != 0)
			zi->ci.dosDate = zipfi->dosDate;
		else
			zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date);
	}

	zi->ci.flag = flagBase;
	if ((level == 8) || (level == 9))
		zi->ci.flag |= 2;
	if ((level == 2))
		zi->ci.flag |= 4;
	if ((level == 1))
		zi->ci.flag |= 6;
	if (password != NULL)
		zi->ci.flag |= 1;

	zi->ci.crc32 = 0;
	zi->ci.method = method;
	zi->ci.encrypt = 0;
	zi->ci.stream_initialised = 0;
	zi->ci.pos_in_buffered_data = 0;
	zi->ci.raw = raw;
	zi->ci.pos_local_header = ZTELL64(zi->z_filefunc, zi->filestream);

	zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment;
	zi->ci.size_centralExtraFree = 32;  // Extra space we have reserved in case we need to add ZIP64 extra info data

	zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree);

	zi->ci.size_centralExtra = size_extrafield_global;
	zip64local_putValue_inmemory(zi->ci.central_header, (uLong)CENTRALHEADERMAGIC, 4);
	/* version info */
	zip64local_putValue_inmemory(zi->ci.central_header + 4, (uLong)versionMadeBy, 2);
	zip64local_putValue_inmemory(zi->ci.central_header + 6, (uLong)20, 2);
	zip64local_putValue_inmemory(zi->ci.central_header + 8, (uLong)zi->ci.flag, 2);
	zip64local_putValue_inmemory(zi->ci.central_header + 10, (uLong)zi->ci.method, 2);
	zip64local_putValue_inmemory(zi->ci.central_header + 12, (uLong)zi->ci.dosDate, 4);
	zip64local_putValue_inmemory(zi->ci.central_header + 16, (uLong)0, 4); /*crc*/
	zip64local_putValue_inmemory(zi->ci.central_header + 20, (uLong)0, 4); /*compr size*/
	zip64local_putValue_inmemory(zi->ci.central_header + 24, (uLong)0, 4); /*uncompr size*/
	zip64local_putValue_inmemory(zi->ci.central_header + 28, (uLong)size_filename, 2);
	zip64local_putValue_inmemory(zi->ci.central_header + 30, (uLong)size_extrafield_global, 2);
	zip64local_putValue_inmemory(zi->ci.central_header + 32, (uLong)size_comment, 2);
	zip64local_putValue_inmemory(zi->ci.central_header + 34, (uLong)0, 2); /*disk nm start*/

	if (zipfi == NULL)
		zip64local_putValue_inmemory(zi->ci.central_header + 36, (uLong)0, 2);
	else
		zip64local_putValue_inmemory(zi->ci.central_header + 36, (uLong)zipfi->internal_fa, 2);

	if (zipfi == NULL)
		zip64local_putValue_inmemory(zi->ci.central_header + 38, (uLong)0, 4);
	else
		zip64local_putValue_inmemory(zi->ci.central_header + 38, (uLong)zipfi->external_fa, 4);

	if (zi->ci.pos_local_header >= 0xffffffff)
		zip64local_putValue_inmemory(zi->ci.central_header + 42, (uLong)0xffffffff, 4);
	else
		zip64local_putValue_inmemory(zi->ci.central_header + 42, (uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset, 4);

	for (i = 0; i < size_filename; i++)
		*(zi->ci.central_header + SIZECENTRALHEADER + i) = *(filename + i);

	for (i = 0; i < size_extrafield_global; i++)
		*(zi->ci.central_header + SIZECENTRALHEADER + size_filename + i) =
			*(((const char*)extrafield_global) + i);

	for (i = 0; i < size_comment; i++)
		*(zi->ci.central_header + SIZECENTRALHEADER + size_filename +
		  size_extrafield_global + i) = *(comment + i);
	if (zi->ci.central_header == NULL)
		return ZIP_INTERNALERROR;

	zi->ci.zip64 = zip64;
	zi->ci.totalCompressedData = 0;
	zi->ci.totalUncompressedData = 0;
	zi->ci.pos_zip64extrainfo = 0;

	err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local);

#ifdef HAVE_BZIP2
	zi->ci.bstream.avail_in = (uInt)0;
	zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE;
	zi->ci.bstream.next_out = (char*)zi->ci.buffered_data;
	zi->ci.bstream.total_in_hi32 = 0;
	zi->ci.bstream.total_in_lo32 = 0;
	zi->ci.bstream.total_out_hi32 = 0;
	zi->ci.bstream.total_out_lo32 = 0;
#endif

	zi->ci.stream.avail_in = (uInt)0;
	zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
	zi->ci.stream.next_out = zi->ci.buffered_data;
	zi->ci.stream.total_in = 0;
	zi->ci.stream.total_out = 0;
	zi->ci.stream.data_type = Z_BINARY;

#ifdef HAVE_BZIP2
	if ((err == ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw))
#else
	if ((err == ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
#endif
	{
		if (zi->ci.method == Z_DEFLATED)
		{
			zi->ci.stream.zalloc = (alloc_func)0;
			zi->ci.stream.zfree = (free_func)0;
			zi->ci.stream.opaque = (voidpf)0;

			if (windowBits > 0)
				windowBits = -windowBits;

			err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy);

			if (err == Z_OK)
				zi->ci.stream_initialised = Z_DEFLATED;
		}
		else if (zi->ci.method == Z_BZIP2ED)
		{
#ifdef HAVE_BZIP2
			// Init BZip stuff here
			zi->ci.bstream.bzalloc = 0;
			zi->ci.bstream.bzfree = 0;
			zi->ci.bstream.opaque = (voidpf)0;

			err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0, 35);
			if (err == BZ_OK)
				zi->ci.stream_initialised = Z_BZIP2ED;
#endif
		}
	}

#ifndef NOCRYPT
	zi->ci.crypt_header_size = 0;
	if ((err == Z_OK) && (password != NULL))
	{
		unsigned char bufHead[RAND_HEAD_LEN];
		unsigned int sizeHead;
		zi->ci.encrypt = 1;
		zi->ci.pcrc_32_tab = get_crc_table();
		/*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/

		sizeHead = crypthead(password, bufHead, RAND_HEAD_LEN, zi->ci.keys, zi->ci.pcrc_32_tab, crcForCrypting);
		zi->ci.crypt_header_size = sizeHead;

		if (ZWRITE64(zi->z_filefunc, zi->filestream, bufHead, sizeHead) != sizeHead)
			err = ZIP_ERRNO;
	}
#endif

	if (err == Z_OK)
		zi->in_opened_file_inzip = 1;
	return err;
}

extern int ZEXPORT zipOpenNewFileInZip4(zipFile file, const char* filename, const zip_fileinfo* zipfi,
										const void* extrafield_local, uInt size_extrafield_local,
										const void* extrafield_global, uInt size_extrafield_global,
										const char* comment, int method, int level, int raw,
										int windowBits, int memLevel, int strategy,
										const char* password, uLong crcForCrypting,
										uLong versionMadeBy, uLong flagBase)
{
	return zipOpenNewFileInZip4_64(file, filename, zipfi,
								   extrafield_local, size_extrafield_local,
								   extrafield_global, size_extrafield_global,
								   comment, method, level, raw,
								   windowBits, memLevel, strategy,
								   password, crcForCrypting, versionMadeBy, flagBase, 0);
}

extern int ZEXPORT zipOpenNewFileInZip3(zipFile file, const char* filename, const zip_fileinfo* zipfi,
										const void* extrafield_local, uInt size_extrafield_local,
										const void* extrafield_global, uInt size_extrafield_global,
										const char* comment, int method, int level, int raw,
										int windowBits, int memLevel, int strategy,
										const char* password, uLong crcForCrypting)
{
	return zipOpenNewFileInZip4_64(file, filename, zipfi,
								   extrafield_local, size_extrafield_local,
								   extrafield_global, size_extrafield_global,
								   comment, method, level, raw,
								   windowBits, memLevel, strategy,
								   password, crcForCrypting, VERSIONMADEBY, 0, 0);
}

extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi,
										   const void* extrafield_local, uInt size_extrafield_local,
										   const void* extrafield_global, uInt size_extrafield_global,
										   const char* comment, int method, int level, int raw,
										   int windowBits, int memLevel, int strategy,
										   const char* password, uLong crcForCrypting, int zip64)
{
	return zipOpenNewFileInZip4_64(file, filename, zipfi,
								   extrafield_local, size_extrafield_local,
								   extrafield_global, size_extrafield_global,
								   comment, method, level, raw,
								   windowBits, memLevel, strategy,
								   password, crcForCrypting, VERSIONMADEBY, 0, zip64);
}

extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi,
										const void* extrafield_local, uInt size_extrafield_local,
										const void* extrafield_global, uInt size_extrafield_global,
										const char* comment, int method, int level, int raw)
{
	return zipOpenNewFileInZip4_64(file, filename, zipfi,
								   extrafield_local, size_extrafield_local,
								   extrafield_global, size_extrafield_global,
								   comment, method, level, raw,
								   -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
								   NULL, 0, VERSIONMADEBY, 0, 0);
}

extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi,
										   const void* extrafield_local, uInt size_extrafield_local,
										   const void* extrafield_global, uInt size_extrafield_global,
										   const char* comment, int method, int level, int raw, int zip64)
{
	return zipOpenNewFileInZip4_64(file, filename, zipfi,
								   extrafield_local, size_extrafield_local,
								   extrafield_global, size_extrafield_global,
								   comment, method, level, raw,
								   -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
								   NULL, 0, VERSIONMADEBY, 0, zip64);
}

extern int ZEXPORT zipOpenNewFileInZip64(zipFile file, const char* filename, const zip_fileinfo* zipfi,
										 const void* extrafield_local, uInt size_extrafield_local,
										 const void* extrafield_global, uInt size_extrafield_global,
										 const char* comment, int method, int level, int zip64)
{
	return zipOpenNewFileInZip4_64(file, filename, zipfi,
								   extrafield_local, size_extrafield_local,
								   extrafield_global, size_extrafield_global,
								   comment, method, level, 0,
								   -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
								   NULL, 0, VERSIONMADEBY, 0, zip64);
}

extern int ZEXPORT zipOpenNewFileInZip(zipFile file, const char* filename, const zip_fileinfo* zipfi,
									   const void* extrafield_local, uInt size_extrafield_local,
									   const void* extrafield_global, uInt size_extrafield_global,
									   const char* comment, int method, int level)
{
	return zipOpenNewFileInZip4_64(file, filename, zipfi,
								   extrafield_local, size_extrafield_local,
								   extrafield_global, size_extrafield_global,
								   comment, method, level, 0,
								   -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
								   NULL, 0, VERSIONMADEBY, 0, 0);
}

local int zip64FlushWriteBuffer(zip64_internal* zi)
{
	int err = ZIP_OK;

	if (zi->ci.encrypt != 0)
	{
#ifndef NOCRYPT
		uInt i;
		int t;
		for (i = 0; i < zi->ci.pos_in_buffered_data; i++)
			zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i], t);
#endif
	}

	if (ZWRITE64(zi->z_filefunc, zi->filestream, zi->ci.buffered_data, zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data)
		err = ZIP_ERRNO;

	zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data;

#ifdef HAVE_BZIP2
	if (zi->ci.method == Z_BZIP2ED)
	{
		zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32;
		zi->ci.bstream.total_in_lo32 = 0;
		zi->ci.bstream.total_in_hi32 = 0;
	}
	else
#endif
	{
		zi->ci.totalUncompressedData += zi->ci.stream.total_in;
		zi->ci.stream.total_in = 0;
	}

	zi->ci.pos_in_buffered_data = 0;

	return err;
}

extern int ZEXPORT zipWriteInFileInZip(zipFile file, const void* buf, unsigned int len)
{
	zip64_internal* zi;
	int err = ZIP_OK;

	if (file == NULL)
		return ZIP_PARAMERROR;
	zi = (zip64_internal*)file;

	if (zi->in_opened_file_inzip == 0)
		return ZIP_PARAMERROR;

	zi->ci.crc32 = crc32(zi->ci.crc32, buf, (uInt)len);

#ifdef HAVE_BZIP2
	if (zi->ci.method == Z_BZIP2ED && (!zi->ci.raw))
	{
		zi->ci.bstream.next_in = (void*)buf;
		zi->ci.bstream.avail_in = len;
		err = BZ_RUN_OK;

		while ((err == BZ_RUN_OK) && (zi->ci.bstream.avail_in > 0))
		{
			if (zi->ci.bstream.avail_out == 0)
			{
				if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
					err = ZIP_ERRNO;
				zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE;
				zi->ci.bstream.next_out = (char*)zi->ci.buffered_data;
			}

			if (err != BZ_RUN_OK)
				break;

			if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw))
			{
				uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32;
				//          uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32;
				err = BZ2_bzCompress(&zi->ci.bstream, BZ_RUN);

				zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo);
			}
		}

		if (err == BZ_RUN_OK)
			err = ZIP_OK;
	}
	else
#endif
	{
		zi->ci.stream.next_in = (Bytef*)buf;
		zi->ci.stream.avail_in = len;

		while ((err == ZIP_OK) && (zi->ci.stream.avail_in > 0))
		{
			if (zi->ci.stream.avail_out == 0)
			{
				if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
					err = ZIP_ERRNO;
				zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
				zi->ci.stream.next_out = zi->ci.buffered_data;
			}

			if (err != ZIP_OK)
				break;

			if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
			{
				uLong uTotalOutBefore = zi->ci.stream.total_out;
				err = deflate(&zi->ci.stream, Z_NO_FLUSH);
				if (uTotalOutBefore > zi->ci.stream.total_out)
				{
					int bBreak = 0;
					bBreak++;
				}

				zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore);
			}
			else
			{
				uInt copy_this, i;
				if (zi->ci.stream.avail_in < zi->ci.stream.avail_out)
					copy_this = zi->ci.stream.avail_in;
				else
					copy_this = zi->ci.stream.avail_out;

				for (i = 0; i < copy_this; i++)
					*(((char*)zi->ci.stream.next_out) + i) =
						*(((const char*)zi->ci.stream.next_in) + i);
				{
					zi->ci.stream.avail_in -= copy_this;
					zi->ci.stream.avail_out -= copy_this;
					zi->ci.stream.next_in += copy_this;
					zi->ci.stream.next_out += copy_this;
					zi->ci.stream.total_in += copy_this;
					zi->ci.stream.total_out += copy_this;
					zi->ci.pos_in_buffered_data += copy_this;
				}
			}
		}  // while(...)
	}

	return err;
}

extern int ZEXPORT zipCloseFileInZipRaw(zipFile file, uLong uncompressed_size, uLong crc32)
{
	return zipCloseFileInZipRaw64(file, uncompressed_size, crc32);
}

extern int ZEXPORT zipCloseFileInZipRaw64(zipFile file, ZPOS64_T uncompressed_size, uLong crc32)
{
	zip64_internal* zi;
	ZPOS64_T compressed_size;
	uLong invalidValue = 0xffffffff;
	short datasize = 0;
	int err = ZIP_OK;

	if (file == NULL)
		return ZIP_PARAMERROR;
	zi = (zip64_internal*)file;

	if (zi->in_opened_file_inzip == 0)
		return ZIP_PARAMERROR;
	zi->ci.stream.avail_in = 0;

	if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
	{
		while (err == ZIP_OK)
		{
			uLong uTotalOutBefore;
			if (zi->ci.stream.avail_out == 0)
			{
				if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
					err = ZIP_ERRNO;
				zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
				zi->ci.stream.next_out = zi->ci.buffered_data;
			}
			uTotalOutBefore = zi->ci.stream.total_out;
			err = deflate(&zi->ci.stream, Z_FINISH);
			zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore);
		}
	}
	else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw))
	{
#ifdef HAVE_BZIP2
		err = BZ_FINISH_OK;
		while (err == BZ_FINISH_OK)
		{
			uLong uTotalOutBefore;
			if (zi->ci.bstream.avail_out == 0)
			{
				if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
					err = ZIP_ERRNO;
				zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE;
				zi->ci.bstream.next_out = (char*)zi->ci.buffered_data;
			}
			uTotalOutBefore = zi->ci.bstream.total_out_lo32;
			err = BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH);
			if (err == BZ_STREAM_END)
				err = Z_STREAM_END;

			zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore);
		}

		if (err == BZ_FINISH_OK)
			err = ZIP_OK;
#endif
	}

	if (err == Z_STREAM_END)
		err = ZIP_OK; /* this is normal */

	if ((zi->ci.pos_in_buffered_data > 0) && (err == ZIP_OK))
	{
		if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO)
			err = ZIP_ERRNO;
	}

	if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
	{
		int tmp_err = deflateEnd(&zi->ci.stream);
		if (err == ZIP_OK)
			err = tmp_err;
		zi->ci.stream_initialised = 0;
	}
#ifdef HAVE_BZIP2
	else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw))
	{
		int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream);
		if (err == ZIP_OK)
			err = tmperr;
		zi->ci.stream_initialised = 0;
	}
#endif

	if (!zi->ci.raw)
	{
		crc32 = (uLong)zi->ci.crc32;
		uncompressed_size = zi->ci.totalUncompressedData;
	}
	compressed_size = zi->ci.totalCompressedData;

#ifndef NOCRYPT
	compressed_size += zi->ci.crypt_header_size;
#endif

	// update Current Item crc and sizes,
	if (compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff)
	{
		/*version Made by*/
		zip64local_putValue_inmemory(zi->ci.central_header + 4, (uLong)45, 2);
		/*version needed*/
		zip64local_putValue_inmemory(zi->ci.central_header + 6, (uLong)45, 2);
	}

	zip64local_putValue_inmemory(zi->ci.central_header + 16, crc32, 4); /*crc*/

	if (compressed_size >= 0xffffffff)
		zip64local_putValue_inmemory(zi->ci.central_header + 20, invalidValue, 4); /*compr size*/
	else
		zip64local_putValue_inmemory(zi->ci.central_header + 20, compressed_size, 4); /*compr size*/

	/// set internal file attributes field
	if (zi->ci.stream.data_type == Z_ASCII)
		zip64local_putValue_inmemory(zi->ci.central_header + 36, (uLong)Z_ASCII, 2);

	if (uncompressed_size >= 0xffffffff)
		zip64local_putValue_inmemory(zi->ci.central_header + 24, invalidValue, 4); /*uncompr size*/
	else
		zip64local_putValue_inmemory(zi->ci.central_header + 24, uncompressed_size, 4); /*uncompr size*/

	// Add ZIP64 extra info field for uncompressed size
	if (uncompressed_size >= 0xffffffff)
		datasize += 8;

	// Add ZIP64 extra info field for compressed size
	if (compressed_size >= 0xffffffff)
		datasize += 8;

	// Add ZIP64 extra info field for relative offset to local file header of current file
	if (zi->ci.pos_local_header >= 0xffffffff)
		datasize += 8;

	if (datasize > 0)
	{
		char* p = NULL;

		if ((uLong)(datasize + 4) > zi->ci.size_centralExtraFree)
		{
			// we can not write more data to the buffer that we have room for.
			return ZIP_BADZIPFILE;
		}

		p = zi->ci.central_header + zi->ci.size_centralheader;

		// Add Extra Information Header for 'ZIP64 information'
		zip64local_putValue_inmemory(p, 0x0001, 2);  // HeaderID
		p += 2;
		zip64local_putValue_inmemory(p, datasize, 2);  // DataSize
		p += 2;

		if (uncompressed_size >= 0xffffffff)
		{
			zip64local_putValue_inmemory(p, uncompressed_size, 8);
			p += 8;
		}

		if (compressed_size >= 0xffffffff)
		{
			zip64local_putValue_inmemory(p, compressed_size, 8);
			p += 8;
		}

		if (zi->ci.pos_local_header >= 0xffffffff)
		{
			zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8);
			p += 8;
		}

		// Update how much extra free space we got in the memory buffer
		// and increase the centralheader size so the new ZIP64 fields are included
		// ( 4 below is the size of HeaderID and DataSize field )
		zi->ci.size_centralExtraFree -= datasize + 4;
		zi->ci.size_centralheader += datasize + 4;

		// Update the extra info size field
		zi->ci.size_centralExtra += datasize + 4;
		zip64local_putValue_inmemory(zi->ci.central_header + 30, (uLong)zi->ci.size_centralExtra, 2);
	}

	if (err == ZIP_OK)
		err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader);

	free(zi->ci.central_header);

	if (err == ZIP_OK)
	{
		// Update the LocalFileHeader with the new values.

		ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc, zi->filestream);

		if (ZSEEK64(zi->z_filefunc, zi->filestream, zi->ci.pos_local_header + 14, ZLIB_FILEFUNC_SEEK_SET) != 0)
			err = ZIP_ERRNO;

		if (err == ZIP_OK)
			err = zip64local_putValue(&zi->z_filefunc, zi->filestream, crc32, 4); /* crc 32, unknown */

		if (uncompressed_size >= 0xffffffff)
		{
			if (zi->ci.pos_zip64extrainfo > 0)
			{
				// Update the size in the ZIP64 extended field.
				if (ZSEEK64(zi->z_filefunc, zi->filestream, zi->ci.pos_zip64extrainfo + 4, ZLIB_FILEFUNC_SEEK_SET) != 0)
					err = ZIP_ERRNO;

				if (err == ZIP_OK) /* compressed size, unknown */
					err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8);

				if (err == ZIP_OK) /* uncompressed size, unknown */
					err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8);
			}
		}
		else
		{
			if (err == ZIP_OK) /* compressed size, unknown */
				err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 4);

			if (err == ZIP_OK) /* uncompressed size, unknown */
				err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 4);
		}

		if (ZSEEK64(zi->z_filefunc, zi->filestream, cur_pos_inzip, ZLIB_FILEFUNC_SEEK_SET) != 0)
			err = ZIP_ERRNO;
	}

	zi->number_entry++;
	zi->in_opened_file_inzip = 0;

	return err;
}

extern int ZEXPORT zipCloseFileInZip(zipFile file)
{
	return zipCloseFileInZipRaw(file, 0, 0);
}

int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip)
{
	int err = ZIP_OK;
	ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset;

	err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)ZIP64ENDLOCHEADERMAGIC, 4);

	/*num disks*/
	if (err == ZIP_OK) /* number of the disk with the start of the central directory */
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)0, 4);

	/*relative offset*/
	if (err == ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, pos, 8);

	/*total disks*/    /* Do not support spawning of disk so always say 1 here*/
	if (err == ZIP_OK) /* number of the disk with the start of the central directory */
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)1, 4);

	return err;
}

int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip)
{
	int err = ZIP_OK;

	uLong Zip64DataSize = 44;

	err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)ZIP64ENDHEADERMAGIC, 4);

	if (err == ZIP_OK)                                                                           /* size of this 'zip64 end of central directory' */
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)Zip64DataSize, 8);  // why ZPOS64_T of this ?

	if (err == ZIP_OK) /* version made by */
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)45, 2);

	if (err == ZIP_OK) /* version needed */
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)45, 2);

	if (err == ZIP_OK) /* number of this disk */
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)0, 4);

	if (err == ZIP_OK) /* number of the disk with the start of the central directory */
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)0, 4);

	if (err == ZIP_OK) /* total number of entries in the central dir on this disk */
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8);

	if (err == ZIP_OK) /* total number of entries in the central dir */
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8);

	if (err == ZIP_OK) /* size of the central directory */
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)size_centraldir, 8);

	if (err == ZIP_OK) /* offset of start of central directory with respect to the starting disk number */
	{
		ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)pos, 8);
	}
	return err;
}
int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip)
{
	int err = ZIP_OK;

	/*signature*/
	err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)ENDHEADERMAGIC, 4);

	if (err == ZIP_OK) /* number of this disk */
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)0, 2);

	if (err == ZIP_OK) /* number of the disk with the start of the central directory */
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)0, 2);

	if (err == ZIP_OK) /* total number of entries in the central dir on this disk */
	{
		{
			if (zi->number_entry >= 0xFFFF)
				err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)0xffff, 2);  // use value in ZIP64 record
			else
				err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)zi->number_entry, 2);
		}
	}

	if (err == ZIP_OK) /* total number of entries in the central dir */
	{
		if (zi->number_entry >= 0xFFFF)
			err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)0xffff, 2);  // use value in ZIP64 record
		else
			err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)zi->number_entry, 2);
	}

	if (err == ZIP_OK) /* size of the central directory */
		err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)size_centraldir, 4);

	if (err == ZIP_OK) /* offset of start of central directory with respect to the starting disk number */
	{
		ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
		if (pos >= 0xffffffff)
		{
			err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)0xffffffff, 4);
		}
		else
			err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset), 4);
	}

	return err;
}

int Write_GlobalComment(zip64_internal* zi, const char* global_comment)
{
	int err = ZIP_OK;
	uInt size_global_comment = 0;

	if (global_comment != NULL)
		size_global_comment = (uInt)strlen(global_comment);

	err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (uLong)size_global_comment, 2);

	if (err == ZIP_OK && size_global_comment > 0)
	{
		if (ZWRITE64(zi->z_filefunc, zi->filestream, global_comment, size_global_comment) != size_global_comment)
			err = ZIP_ERRNO;
	}
	return err;
}

extern int ZEXPORT zipClose(zipFile file, const char* global_comment)
{
	zip64_internal* zi;
	int err = 0;
	uLong size_centraldir = 0;
	ZPOS64_T centraldir_pos_inzip;
	ZPOS64_T pos;

	if (file == NULL)
		return ZIP_PARAMERROR;

	zi = (zip64_internal*)file;

	if (zi->in_opened_file_inzip == 1)
	{
		err = zipCloseFileInZip(file);
	}

#ifndef NO_ADDFILEINEXISTINGZIP
	if (global_comment == NULL)
		global_comment = zi->globalcomment;
#endif

	centraldir_pos_inzip = ZTELL64(zi->z_filefunc, zi->filestream);

	if (err == ZIP_OK)
	{
		linkedlist_datablock_internal* ldi = zi->central_dir.first_block;
		while (ldi != NULL)
		{
			if ((err == ZIP_OK) && (ldi->filled_in_this_block > 0))
			{
				if (ZWRITE64(zi->z_filefunc, zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block)
					err = ZIP_ERRNO;
			}

			size_centraldir += ldi->filled_in_this_block;
			ldi = ldi->next_datablock;
		}
	}
	free_linkedlist(&(zi->central_dir));

	pos = centraldir_pos_inzip - zi->add_position_when_writting_offset;
	if (pos >= 0xffffffff)
	{
		ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc, zi->filestream);
		Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip);

		Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos);
	}

	if (err == ZIP_OK)
		err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip);

	if (err == ZIP_OK)
		err = Write_GlobalComment(zi, global_comment);

	if (ZCLOSE64(zi->z_filefunc, zi->filestream) != 0)
		if (err == ZIP_OK)
			err = ZIP_ERRNO;

#ifndef NO_ADDFILEINEXISTINGZIP
	TRYFREE(zi->globalcomment);
#endif
	TRYFREE(zi);

	return err;
}

extern int ZEXPORT zipRemoveExtraInfoBlock(char* pData, int* dataLen, short sHeader)
{
	char* p = pData;
	int size = 0;
	char* pNewHeader;
	char* pTmp;
	short header;
	short dataSize;

	int retVal = ZIP_OK;

	if (pData == NULL || *dataLen < 4)
		return ZIP_PARAMERROR;

	pNewHeader = (char*)ALLOC(*dataLen);
	pTmp = pNewHeader;

	while (p < (pData + *dataLen))
	{
		header = *(short*)p;
		dataSize = *(((short*)p) + 1);

		if (header == sHeader)  // Header found.
		{
			p += dataSize + 4;  // skip it. do not copy to temp buffer
		}
		else
		{
			// Extra Info block should not be removed, So copy it to the temp buffer.
			memcpy(pTmp, p, dataSize + 4);
			p += dataSize + 4;
			size += dataSize + 4;
		}
	}

	if (size < *dataLen)
	{
		// clean old extra info block.
		memset(pData, 0, *dataLen);

		// copy the new extra info block over the old
		if (size > 0)
			memcpy(pData, pNewHeader, size);

		// set the new extra info size
		*dataLen = size;

		retVal = ZIP_OK;
	}
	else
		retVal = ZIP_ERRNO;

	TRYFREE(pNewHeader);

	return retVal;
}
