📄 zipbuilder.cpp
字号:
/* TZipBuilder component v1.60 by Chris Vleghert
* a C++ Builder 1, 3, 4 , 5 and 6 wrapper for the freeware ZIP and UNZIP DLL's
* from Chris Vleghert and E.W. Engler.
* e-mail: englere@abraxis.com
* www: http://www.geocities.com/SiliconValley/Network/2114
* v1.72 by Roger Aelbrecht Februari 18, 2003.
* http://web.wanadoo.be/driehoeksw
*/
#include <vcl\vcl.h>
#pragma hdrstop
#pragma package(smart_init) // Used in BCB 3,4,5 ignored in BCB 1
#pragma resource "*.res"
#include "ZIPBuilder.h"
#include "ZipMsg.h"
#include "ZipBuildDefs.h"
//USEUNIT("CallBack.cpp");
//USEUNIT("ZipDLL.cpp");
//USEUNIT("UnZipDLL.cpp");
// Added by Russell Peters to compile/build under BCB6
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#if __BORLANDC__ > 0x0550
#define STRMSIZ(x) __int64(x)
#else
#define STRMSIZ(x) x
#endif
//end of add
// ================== changed or New functions =============================
// TZipBuilder::GetZipComment---------------------------------------------------
// 1.73.3.2 11 Oct 2003 RP comment now converted when read
// 1.73 (2 June 2003) RA use ConvertOEM
String __fastcall TZipBuilder::GetZipComment(void)
{
// return ConvertOEM(FZipComment, cpdOEM2ISO);
return FZipComment;
}
// TZipBuilder::GetZipComment
// TZipBuilder::SetZipComment---------------------------------------------------
// 1.73.3.2 11 Oct 2003 RP allow preset comment
// 1.73 (10 June 2003) RA bugfix
// changed 1.73 (22 May 2003) RA use of ReadJoin
void __fastcall TZipBuilder::SetZipComment(String zComment)
{
struct ZipEndOfCentral EOC;
bool Fatal = false;
FInFileHandle = -1;
try
{
if(!FZipFileName.Length() ) throw EZipBuilder(GE_NoZipSpecified);
#ifndef NO_SPAN
// for multi volume find last volume and allow that file does not exists
GetLastVolume(FZipFileName, EOC, true); // wil read existing comment
#else
OpenEOC(EOC,false);
#endif
FZipComment = zComment;
if(FInFileHandle != -1)
{
FileClose(FInFileHandle); // must reopen for read/write
AnsiString CommentBuf = ConvertOEM(zComment, cpdISO2OEM);
if(CommentBuf == FEOCComment) return; // same - nothing to do
int len = CommentBuf.Length();
FInFileHandle = FileOpen( FInFileName, fmShareDenyWrite | fmOpenReadWrite );
if(FInFileHandle == -1)
throw EZipBuilder(DS_FileOpen);
if(FileSeek(FInFileHandle, (SeekInt)FZipEOC, 0 ) == -1)
throw EZipBuilder(DS_FailedSeek);
ReadJoin( &EOC, sizeof(EOC), DS_EOCBadRead);
EOC.ZipCommentLen = len;
if(FileSeek(FInFileHandle, -(SeekInt)(signed)sizeof(EOC), 1) == -1)
throw EZipBuilder(DS_FailedSeek);
Fatal = true;
if(FileWrite(FInFileHandle, &EOC, sizeof(EOC)) != sizeof(EOC))
throw EZipBuilder(DS_EOCBadWrite);
if(FileWrite( FInFileHandle, FZipComment.c_str(), len ) != len )
throw EZipBuilder(DS_NoWrite);
Fatal = false;
// if SetEOF fails we get garbage at the end of the file, not nice but
// also not important.
SetEndOfFile((HANDLE)FInFileHandle);
}
}
catch (const EZipBuilder &ezl)
{
ShowExceptionError(ezl);
FZipComment = "";
}
catch (const MEMEXCEPT &me)
{
ShowZipMessage(GE_NoMem);
FZipComment = "";
}
if(FInFileHandle != -1) FileClose(FInFileHandle);
if(Fatal) // Try to read the zipfile, maybe it still works.
_List();
}
// TZipBuilder::SetZipComment
// TZipBuilder::OpenEOC--------------------------------------------------------
// 1.73.3.2 8 Oct 2003 RA RP allow max 512 whitespace chars at end of file and save comment
// changed 1.73 (22 May 2003) RA use ReadJoin
// Function to find the EOC record at the end of the archive (on the last disk.)
// We can get a return value( true::Found, false::Not Found ) or an exception if not found.
bool __fastcall TZipBuilder::OpenEOC(struct ZipEndOfCentral &EOC, bool DoExcept)
{
unsigned long Sig;
int DiskNo = 0;
bool First = false;
AnsiString ZipBuf;
FZipComment = "";
FZipEOC = 0;
FEOCComment = "";
// Open the input archive, presumably the last disk.
if((FInFileHandle = FileOpen(FInFileName, fmShareDenyWrite | fmOpenRead )) == -1)
{
if(DoExcept) throw EZipBuilder(DS_NoInFile);
ShowZipMessage(DS_FileOpen);
return false;
}
// First a check for the first disk of a spanned archive,
// could also be the last so we don't issue a warning yet.
if(FileRead(FInFileHandle, &Sig, 4) == 4 && Sig == ExtLocalSig &&
FileRead(FInFileHandle, &Sig, 4) == 4 && Sig == LocalFileHeaderSig)
{
First = true;
FIsSpanned = true;
}
// Next we do a check at the end of the file to speed things up if
// there isn't a Zip archive comment.
if((FFileSize = (long)FileSeek(FInFileHandle, -(SeekInt)(signed)sizeof(EOC), 2 )) != -1 )
{
FFileSize += sizeof(EOC); // Save the archive size as a side effect.
FRealFileSize = FFileSize; // There could follow a correction on FFileSize.
if(FileRead(FInFileHandle, &EOC, sizeof(EOC)) == sizeof(EOC) &&
EOC.HeaderSig == EndCentralDirSig)
{
FZipEOC = FFileSize - sizeof(EOC);
return true;
}
}
// Now we try to find the EOC record within the last 65535 + sizeof( EOC ) bytes
// of this file because we don't know the Zip archive comment length at this time.
try
{
int Size = min(FFileSize, (long)(65535 + sizeof(EOC)));
ZipBuf.SetLength(Size);
if(FileSeek(FInFileHandle, (SeekInt)(-Size), 2) == -1) throw EZipBuilder(DS_FailedSeek);
ReadJoin(ZipBuf.c_str(), Size,DS_EOCBadRead);
for(int i = Size - sizeof(EOC)- 1; i >= 0; i--)
{
ZipEndOfCentral* pEOC = reinterpret_cast<ZipEndOfCentral*>(ZipBuf.c_str() + i);
if(pEOC->HeaderSig == EndCentralDirSig)
{
FZipEOC = FFileSize - Size + i;
memcpy(&EOC, ZipBuf.c_str() + i, sizeof(EOC));
//If we have ZipComment: Save it,No codepage translation yet, wait for CEH read.
if(EOC.ZipCommentLen)
FEOCComment = ZipBuf.SubString(i + sizeof(EOC) + 1 , EOC.ZipCommentLen);
// Check if we really are at the end of the file, if not correct the filesize
// and give a warning. (It should be an error but we are nice.)
if(i + sizeof(EOC) + EOC.ZipCommentLen - Size)
{
FFileSize += (i + sizeof(EOC) + (unsigned)EOC.ZipCommentLen - Size);
// Now we need a check for WinZip Self Extractor which makes SFX files which
// allmost always have garbage at the end (Zero filled at 512 byte boundary!)
// In this special case 'we' don't give a warning.
bool ShowGarbageMsg = true;
// if(FRealFileSize - (unsigned)FFileSize < 512 && (FRealFileSize % 512) == 0)
if(FRealFileSize - (unsigned)FFileSize < 512)
{
int j = i + sizeof(EOC) + EOC.ZipCommentLen;
// while(ZipBuf[j] == '\0' && j <= Size) j++;
while(ZipBuf[j] < '/' && j <= Size) j++;
if(j == Size + 1) ShowGarbageMsg = false;
}
if(ShowGarbageMsg) ShowZipMessage(LI_GarbageAtEOF);
}
// If we have a ZipComment save it, must be after Garbage check because a '\0' is set!
/* if(EOC.ZipCommentLen)
{
ZipBuf[i + sizeof(EOC) + EOC.ZipCommentLen] = '\0';
FZipComment = AnsiString(ZipBuf + i + sizeof(EOC)); // No codepage translation yet, wait for CEH read.
} */
// delete[] ZipBuf;
return true;
}
}
// delete[] ZipBuf;
}
catch ( ... )
{
// delete[] ZipBuf;
if(DoExcept) throw;
}
if(DoExcept)
{ // Get the volume number if it's disk from a set. - 1.72 moved
if(FVolumeName.SubString(1, 8) == "PKBACK# ")
DiskNo = StrToIntDef(FVolumeName.SubString(9, 3), 0);
else
{
AnsiString ext = UpperCase(ExtractFileExt(FInFileName));
DiskNo = 0;
if(ext.SubString(1,2) == ".Z")
DiskNo = StrToIntDef(ext.SubString(2,2),0);
if(DiskNo <= 0)
DiskNo = StrToIntDef(FInFileName.SubString(FInFileName.Length() -
ext.Length() - 3 +1, 3),0);
}
if(!First && DiskNo) throw EZipBuilder(DS_NotLastInSet, DiskNo);
throw EZipBuilder((First) ? ((DiskNo == 1) ? DS_FirstInSet : DS_FirstFileOnHD) : DS_NoValidZip);
}
return false;
}
// TZiPBuidler::OpenEOC
// TZipBuilder::_List-----------------------------------------------------------
// 1.73.3.2 Oct 11 2003 RP convert saved comment
// 1.73 (26 July 2003) RA - added test for empty ZipFileName
// 1.73 (15 July 2003) RP / RA ReadJoin
void __fastcall TZipBuilder::_List(void) // All work is local - no DLL calls.
{
struct ZipEndOfCentral EOC;
struct ZipCentralHeader CEH;
ZipDirEntry *pzd = NULL;
unsigned long OffsetDiff = 0;
char Name[ MAX_PATH ]; //, *fc = NULL;
bool LiE = false;
// Can't do LIST at design time.
if(ComponentState.Contains(csDesigning)) return;
// Zero out any previous entries.
FreeZipDirEntryRecords();
FRealFileSize = 0;
FZipSOC = 0;
FSFXOffset = 0; //must be before the following "if"
// FZipComment = ""; //done in openEOC
FIsSpanned = false;
FDirOnlyCount = 0;
ErrCode = 0; // reset previous error code
char MadeOS = 0; // 1.73.3.2
char MadeVer = 20; // 1.73.3.2
if(FZipFileName =="")
{
if(FOnDirUpdate) FOnDirUpdate(this);
return;
}
#ifndef NO_SPAN
// Locate last of multi volume or last disk of spanned
int Result = GetLastVolume(FZipFileName,EOC,true);
if(Result == -1) return; // error exception should been thrown when detected;
if(Result == 1) // Don't complain - this may intentionally be a new zip file.
#else
if(!FileExists(FZipFileName))
#endif
{
if(FOnDirUpdate) FOnDirUpdate(this);
return;
}
try
{
StartWaitCursor();
try
{
#ifdef NO_SPAN
FInFileName = FZipFileName; // to open EOC
if(!OpenEOC(EOC, false)) // if no EOC then not valid zip
throw EZipBuilder(DS_NoValidZip, true);
#endif
FTotalDisks = EOC.ThisDiskNo; // Needed if GetNewDisk() is called.
// This could also be set to True if it's the first and only disk.
if(EOC.ThisDiskNo > 0) FIsSpanned = true;
// Do we have to request for a previous disk first?
if(EOC.ThisDiskNo != EOC.CentralDiskNo)
{
#ifndef NO_SPAN
GetNewDisk(EOC.CentralDiskNo);
FFileSize = FileSeek(FInFileHandle, 0, 2); //v1.52j
OffsetDiff = EOC.CentralOffset; //v1.52j
#else
throw EZipBuilder(DS_NoDiskSpan, true);
#endif
}
else //v1.52j
// Due to the fact that v1.3, v1.4 and v1.45 programs do not change the archives
// EOC and CEH records in case of a SFX conversion (and back) we have to
// make this extra check.
OffsetDiff = FFileSize - EOC.CentralSize - sizeof(EOC) - EOC.ZipCommentLen;
FWrongZipStruct = false;
if(EOC.CentralOffset != OffsetDiff)
{ // Issue a warning only.
FWrongZipStruct = true; // We need this in the ConvertXxx functions.
ShowZipMessage( LI_WrongZipStruct );
}
// Now we can go to the start of the Central directory.
if(FileSeek(FInFileHandle, (SeekInt)OffsetDiff, 0 ) == -1 )
throw EZipBuilder(LI_ReadZipError);
FZipSOC = OffsetDiff;
FSFXOffset = FFileSize;
if(FFileSize == 22) FSFXOffset = 0; //v1.52L
// Read every entry: The central header and save the information.
FZipContents->Capacity = EOC.TotalEntries;
for(int i = 0; i < EOC.TotalEntries; i++)
{
// Read a central header.
while(FileRead(FInFileHandle, &CEH, sizeof(CEH)) != sizeof(CEH))
{ //v1.52j
// It's possible that we have the central header split up.
if(FDiskNr >= EOC.ThisDiskNo) throw EZipBuilder(DS_CEHBadRead);
// We need the next disk with central header info.
#ifndef NO_SPAN
GetNewDisk(FDiskNr + 1);
#else
throw EZipBuilder(DS_NoDiskSpan, true);
#endif
}
if(CEH.HeaderSig != CentralFileHeaderSig) throw EZipBuilder(DS_CEHWrongSig);
// Now the filename.
ReadJoin( Name, CEH.FileNameLen,DS_CENameLen);
// Save version info globally for use by codepage translation routine
FHostNum = CEH.VersionMadeBy1;
FHostVer = CEH.VersionMadeBy0;
if(FHostNum > 0) // not msdos
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -