//---------------------------------------------------------------------------

#pragma hdrstop
#include <string.h>

#include "SFXProcs.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)

#define min(x,y) ((x)<(y)?(x):(y))

/*----------------------------------------------------------------------------
3.2.3.1 28 Nov 2004 RA corrected size of buffer needs to be MAX_PATH
*/
char* StrGetEditText(HWND wndPar)
{
  char* Result = new char[MAX_PATH];
  GetDlgItemText(wndPar, ID_EDITBOX, Result, MAX_PATH);
  return Result;
}
/*----------------------------------------------------------------------------
3.2.3.1 25 Nov 2004 RA test if 1st disk already present for MV copied to floppy
3.2.0.1 7 April 2004 RA changed use of VStr_DetachName and VStr_DetachExt variables
added version 3.0 May 1, 2003
function to get new disk from the user*/
void GetNewDisk(HWND wndOwner, int DiskSeq)
{
  if(!CloseHandle(VH_InFile)) ErrorHalt(Err_CannotCloseFile, VStr_ExeName);
	do{
     if((VInt_SpanType == SPAN_TYPE_MULTI_VOL) && !DiskSeq)
     {  // check if 1st disk is already there for MV copied to floppy
      strcpy(VStr_ExeName,VStr_DetachName);
      sprintf(PathBuf,"%03d",1 + DiskSeq);
      strcat(VStr_ExeName, PathBuf);
      strcat(VStr_ExeName, VStr_DetachExt);
      if(IsRightDisk(DiskSeq)) break;
     }
     if(!(drt == DRIVE_FIXED || drt == DRIVE_REMOTE || drt == DRIVE_RAMDISK))
     {
      sprintf(PathBuf, Msg_InsertDisk,DiskSeq + 1,drive);
      if(MsgBox(wndOwner,PathBuf ,VStr_SFX_Caption, MB_OKCANCEL) == IDCANCEL)
      {
        if(VH_TempFile != INVALID_HANDLE_VALUE)
        {
          if(!CloseHandle(VH_TempFile))
          {
            FormatStr(Err_CannotCloseFile, Tempfile, TempBuf);
            ErrorMsgBox(wndOwner, TempBuf);
          }
          DeleteFile(Tempfile);
        }
        SfxExit(false, true);
      }
     }
     if(VInt_SpanType == SPAN_TYPE_MULTI_VOL)
     {
      strcpy(VStr_ExeName,VStr_DetachName);
      sprintf(PathBuf,"%03d",1 + DiskSeq);
      strcat(VStr_ExeName, PathBuf);
      strcat(VStr_ExeName, VStr_DetachExt);
     }
     else
     {
      strcpy(VStr_ExeName, VStr_DetachName);
      strcat(VStr_ExeName, VStr_DetachExt);
     }
  }while(!IsRightDisk(DiskSeq));
		// Open the input archive on this disk.
 if((VH_InFile = CreateFile(VStr_ExeName, GENERIC_READ, FILE_SHARE_READ, NULL,
                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)) == INVALID_HANDLE_VALUE)
       ErrorHalt(Err_CannotOpenFile, VStr_ExeName);
    // assume no shifted offset on detached archives
  VDW_OffsetDelta = 0;
}

/*----------------------------------------------------------------------------
3.2.3.1 25 Nov 2004 RA returned result for spanned type corrected
3.2.0.1 7 April 2004 RA changed to use variables VStr_DetachName and VStr_DetachExe
added version 3.0 May 1, 2003
function to check if the inserted disk is the right disk*/
bool IsRightDisk(int DiskSeq)
{
  // create volume sequence number
  sprintf(PathBuf, "%03d",1 + DiskSeq);
  strcpy(TempBuf, VStr_DetachName);
  strcat(TempBuf, VStr_DetachExt);
  if(VInt_SpanType == SPAN_TYPE_SPANNED)
  {
    unsigned long Dummy1, Dummy2, DiskSerial;
    char VolName[MAX_PATH];
    GetVolumeInformation(drive, VolName, MAX_PATH, &DiskSerial, &Dummy1, &Dummy2, NULL, 0);
    char DiskVolName[12];
    strcpy(DiskVolName, "PKBACK# ");
    strcat(DiskVolName, PathBuf);
    int res = strcmp(VolName, DiskVolName);
    if((res == 0) && (strcmp(VStr_ExeName, TempBuf) != 0))
    {
      if(!CloseHandle(VH_InFile)) ErrorHalt(Err_CannotCloseFile, VStr_ExeName);
      strcpy(VStr_ExeName, TempBuf);
      		// Open the input archive on this disk.
      if((VH_InFile = CreateFile(VStr_ExeName, GENERIC_READ, FILE_SHARE_READ, NULL,
                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)) == INVALID_HANDLE_VALUE)
            ErrorHalt(Err_CannotOpenFile, VStr_ExeName);
            // assume no shifted offset on detached archives
      VDW_OffsetDelta = 0;
    }
    return (res == 0) ? true : false;
  }
  else
  {
    if(!VInt_SpanType) // not spanned
       return (strcmp(VStr_ExeName, TempBuf) == 0) ? true : false;
    strcpy(TempBuf, VStr_DetachName);
    strcat(TempBuf, PathBuf);
    strcat(TempBuf, VStr_DetachExt);
    return (strcmp(VStr_ExeName, TempBuf) == 0) ? true : false;
  }
}

/*----------------------------------------------------------------------------
3.2.3.1 25 Nov 2004 RA use of DetachName and DetachExt corrected
3.2.0.1 7 April 2004 RA changed
added version 3.0 May 1, 2003
test if file is a spanned sfx file*/
void CheckIfSpanned(void)
{
  VInt_SpanType = SPAN_TYPE_MULTI_VOL;
  if(VRec_SFXHeader.Options & soDetached)
  {
    strcpy(PathBuf, VStr_DetachName);
    strcat(PathBuf, VStr_DetachExt);
    VStr_SourceDir = new char[strlen(PathBuf) + 1];
    ExtractFilePath(PathBuf, VStr_SourceDir);
    AppendDirSeparator(VStr_SourceDir);
    fnsplit(VStr_SourceDir,drive,NULL,NULL,NULL);
    drt = GetDriveType(strcat(drive,"\\"));
    if(!(drt == DRIVE_FIXED || drt == DRIVE_REMOTE || drt == DRIVE_RAMDISK))
    {
      strcpy(PathBuf, VStr_DetachName);
      strcat(PathBuf, "001");
      strcat(PathBuf, VStr_DetachExt);
      if(!SfxFileExists(PathBuf)) VInt_SpanType = SPAN_TYPE_SPANNED;
    }
  }
  else ErrorHalt(Err_Archive, VStr_ExeName);
}

//----------------------------------------------------------------------------
// 3.2.0.1 7 April 2004 RA added
// added version 3.0 May 1, 2003
// spanned archive, extract local header and file data to a temporary file
void ExtractToTempFile(const HWND wndOwner, unsigned& LocalOffset, HANDLE& OldHandle)
{
  unsigned LocalHeaderSig = ZipLocalHeaderSig;
  --LocalHeaderSig; //correct sig only in RAM
  if(!strlen(Tempfile))
  {
    memset(TempFileBuf,0,BufSize);
    // create a temporary filename
    char* temp = new char[7];
    strcpy(temp,"%temp%");
    temp = ExpandEnv(temp);
    AppendDirSeparator(temp);
    if(GetTempFileName(temp, "SFX", 0, Tempfile))
          DeleteFile(Tempfile); // because created by GetTempFileName
    else
    {
      delete[] temp;
      ErrorHalt(Err_CannotOpenFile, Tempfile);
    }
    delete[] temp;
  }
    // create temp file to copy the deflated file from the archive and prepare it for inflate
  if((VH_TempFile = CreateFile(Tempfile, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                         CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, 0)) == INVALID_HANDLE_VALUE)
       ErrorHalt(Err_CannotOpenFile, Tempfile);
  FSeek(LocalOffset, FILE_BEGIN);
  // read the local header
  int disknr = VRec_ZipHeader.DiskStart;
  RWJoinData(wndOwner, TempFileBuf, sizeof(TZipLocalHeader), disknr,true);
  TZipLocalHeader* LOH = reinterpret_cast<TZipLocalHeader*>(TempFileBuf);
  if(LOH->HeaderSig != LocalHeaderSig)
        ErrorHalt(Err_WrongFileSize, VStr_ExeName);
  unsigned DataToCopy = VRec_ZipHeader.ComprSize + LOH->FileNameLen + LOH->ExtraLen;
   // if file is encrypted then the ZipDataDescriptor must also be copied is needed in inflate
  if(LOH->Flag & 0x0008) DataToCopy += sizeof(TZipExtLocalHeader);
  RWJoinData(wndOwner, TempFileBuf, DataToCopy, disknr, true);
  OldHandle = VH_InFile;
  VH_InFile = VH_TempFile;
  LocalOffset = 0;
}
//----------------------------------------------------------------------------
// 3.2.0.1 7 April 2004 RA added
// reattach current archive and close tempfile
void RollBackTempFile(const HWND wndOwner, const HANDLE OldHandle)
{
  VH_InFile = OldHandle;
  if(!CloseHandle(VH_TempFile))
  {
    FormatStr(Err_CannotCloseFile, Tempfile, TempBuf);
    ErrorMsgBox(wndOwner, TempBuf);
  }
  DeleteFile(Tempfile);
}

//----------------------------------------------------------------------------
// added version 3.0 May 1, 2003
// read data and copy in temp file if needed or skip to next local header
void RWJoinData(HWND wndOwner,char *Buffer, int ReadLen, int& DiskNbr, bool Copy)
{
  int SizeR, ToRead;
	while(ReadLen > 0)
	{
		ToRead = min(ReadLen, BufSize);
		if((SizeR = FRead(Buffer, ToRead)) != ToRead)
		{
      // Check if we are at the end of a input disk.
      if(FSeek(0, FILE_CURRENT) != FSeek(0,FILE_END))
              ErrorHalt(Err_WrongFileSize, "");
      // It seems we are at the end, so get a next disk.
			GetNewDisk(wndOwner, ++DiskNbr);
		}
		if(SizeR > 0)
		{
			if(Copy)
			{
				unsigned long BytesWritten;
  			if(!WriteFile(VH_TempFile, Buffer, SizeR, &BytesWritten, NULL))
        			ErrorHalt(Err_WriteFile, Tempfile);
			}
			ReadLen -= SizeR;
		}
	}
}
//----------------------------------------------------------------------------
// 3.2.0.1 7 April 2004 RA Added
// open the correct archive in spanned, multivolume or detached sfx's
void OpenRightArchive(HWND wndOwner, const int DiskNumber)
{
  VH_TempFile = INVALID_HANDLE_VALUE; // prepare for opening
  if(!IsRightDisk(DiskNumber))
     GetNewDisk(wndOwner, DiskNumber); // need another disk
}
//----------------------------------------------------------------------------
// show a message box
int MsgBox(const HWND wndPar, const char* sMsg,const char* sTitle,
           const unsigned uType)
{
  return MessageBox(wndPar, sMsg, sTitle, uType | MB_TASKMODAL);
}
//----------------------------------------------------------------------------
// show an error message
void ErrorMsgBox(const HWND wndPar,const char* sMsg)
{
  MsgBox(wndPar, sMsg, Cap_Err, MB_ICONSTOP);
}
//---------------------------------------------------------------------------
// fatal error, exit
void ErrorHalt(const char* Msg, const char* Arg)
{
  FormatStr(Msg, Arg, PathBuf);
  if(VH_InFile && (VH_InFile != INVALID_HANDLE_VALUE))  CloseHandle(VH_InFile);
  VH_InFile = INVALID_HANDLE_VALUE;
  ErrorMsgBox(0, PathBuf);
  SfxExit(false,true);
}
//----------------------------------------------------------------------------
void Make_CRC32Table(void)
{
  for(int i = 0; i < UCHARMAX; ++i)
  {
    unsigned long r = i;
    for(int j = CHARBIT; j; j--)
    {
      r = r & 1 ? CRCPOLY ^ ( r >> 1) : r >> 1;
      VArr_CRC32Table[i] = r;
    }
  }
}
//----------------------------------------------------------------------------
// 3.2.0.1 11 April 2004 RA reading detached info added
// read TSFXFileHeader from input file
void GetDefParams(void) // reads the values from the special header
{
  VInt_FileBegin = GetExeSize();
  // for debugging use the real size of the stub
//  VInt_FileBegin = 81920;
  FSeek(VInt_FileBegin, FILE_BEGIN);
  memset(&VRec_SFXHeader, sizeof(VRec_SFXHeader), 0);
  FRead(&VRec_SFXHeader, sizeof(VRec_SFXHeader));
  if(VRec_SFXHeader.Signature != SFX_HEADER_SIG)
     ErrorHalt(Err_WrongFileSize, "");
  VInt_FileBegin += VRec_SFXHeader.Size;
  VStr_SFX_Caption = ReadSFXString(VRec_SFXHeader.CaptionSize);
  VStr_SFX_Path = ReadSFXString(VRec_SFXHeader.PathSize);
  VStr_SFX_CmdLine = ReadSFXString(VRec_SFXHeader.CmdLineSize);
  VStr_SFX_RegFailPath = ReadSFXString(VRec_SFXHeader.RegFailPathSize);
  VStr_SFX_StartMsg = ReadSFXString(VRec_SFXHeader.StartMsgSize);
   //get the path from registry, added 10/10/98 ##FR
  if(VRec_SFXHeader.PathSize)
  {
    char temp[3] = {0,0,0};
    if(CompareText("HK",strncpy(temp,VStr_SFX_Path, 2)))
    {
      VStr_SFX_Path = GetPathFromRegistry(VStr_SFX_Path);
      if(!VStr_SFX_Path)
      {
        if(VRec_SFXHeader.RegFailPathSize > 0)
        {
          VStr_SFX_Path = new char[strlen(VStr_SFX_RegFailPath) +1];
          strcpy(VStr_SFX_Path, VStr_SFX_RegFailPath);
        }
        else
        {
          delete[] VStr_SFX_Path;
          VStr_SFX_Path = new char[3];
          strcpy(VStr_SFX_Path,"><"); // substitue to temp path below
        }
      }
    }
    while(strstr(VStr_SFX_Path, "><") != NULL)
    {
      char* temp = new char[7];
      strcpy(temp,"%temp%");
      temp = ExpandEnv(temp);
      AppendDirSeparator(temp);
      FormatStr(VStr_SFX_Path, temp, TempBuf);
      delete[] temp;
      delete[] VStr_SFX_Path;
      VStr_SFX_Path = new char[strlen(TempBuf)+ 1];
      strcpy(VStr_SFX_Path, TempBuf);
    }
  }
  if(VRec_SFXHeader.Options & soExpandVariables)
  {
    VStr_SFX_Path = ExpandEnv(VStr_SFX_Path);
    VStr_SFX_Caption = ExpandEnv(VStr_SFX_Caption);
    VStr_SFX_StartMsg = ExpandEnv(VStr_SFX_StartMsg);
    VStr_SFX_CmdLine = ExpandEnv(VStr_SFX_CmdLine);
  }
  if(!VRec_SFXHeader.PathSize) VStr_ExtractPath = GetCurDir();
  else
  {
    if(CompareText(VStr_SFX_Path, "<VOLATILE>"))
                   VStr_ExtractPath = GetUniqueVolatilePath();
    else VStr_ExtractPath = ExpandPath(VStr_SFX_Path);
  }
  // shall we show the message ?
  if(VRec_SFXHeader.StartMsgSize)
  {
    int ReturnValue = MsgBox(0, VStr_SFX_StartMsg,VStr_SFX_Caption, VRec_SFXHeader.StartMsgType);
    if((ReturnValue == IDCANCEL) || (ReturnValue == IDABORT) || (ReturnValue == IDNO))
      SfxExit(true, true);
  }
  // check autorun flag
  if((VRec_SFXHeader.Options & soAutoRun) && (VRec_SFXHeader.Options & soCheckAutoRunFileName))
  {
    ExtractFileName(VStr_ExeName, PathBuf);
    if(!(CompareText(PathBuf, "Setup.exe") || (PathBuf[0] == '!')))
         VRec_SFXHeader.Options &= 0xFFF7; // reset soAutoRun
  }
  FSeek(VInt_FileBegin, FILE_BEGIN);
    // check dword boundary
  unsigned Sig;
  FRead(&Sig, sizeof(Sig));
  if(Sig != SFX_HEADER_END_SIG)
     ErrorHalt(Err_WrongFileSize, "");
    // skip end of header
  VInt_FileBegin = FSeek(sizeof(unsigned), FILE_CURRENT);
    // check if we have a detached header
  if(VRec_SFXHeader.Options & soDetached)
  {
    TSFXDetachedHeader recDetach;
    memset(&recDetach, sizeof(recDetach), 0);
    FRead(&recDetach, sizeof(recDetach));
    if(recDetach.Signature != SFX_DETACHED_HEADER_SIG)
          ErrorHalt(Err_WrongFileSize, "");
    VStr_DetachName = ReadSFXString(recDetach.NameLen);
    ExtractFilePath(VStr_ExeName, PathBuf);
    AppendDirSeparator(PathBuf);
    strcat(PathBuf, VStr_DetachName);
    delete[] VStr_DetachName; // release previous buffer
    VStr_DetachName = new char[strlen(PathBuf) + 1]; // new buff
    strcpy(VStr_DetachName, PathBuf);
    VStr_DetachExt = ReadSFXString(recDetach.ExtLen);
    strcpy(PathBuf, ".");
    strcat(PathBuf, VStr_DetachExt);
    delete[] VStr_DetachExt;
    VStr_DetachExt = new char[strlen(PathBuf) + 1];
    strcpy(VStr_DetachExt, PathBuf);
      // skip end of header
    VInt_FileBegin = FSeek(sizeof(recDetach), FILE_CURRENT);
  }
}
//--------------------------------------------------------------------------
// from Angus Johnson's TZip-SFX code:
// get the executable's file size to get rid of caring about the exe size
unsigned GetExeSize(void)
{
  unsigned Result = 0;

  char* p = reinterpret_cast<char *>(MInstance);
 // definitions in winnt.h
  p += reinterpret_cast<PIMAGE_DOS_HEADER>(p)->e_lfanew + sizeof(UINT);
  int NumSections = reinterpret_cast<PIMAGE_FILE_HEADER>(p)->NumberOfSections;
  p += sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER);
  for(int i = 1; i <= NumSections; i++)
  {
    PIMAGE_SECTION_HEADER SectionH = reinterpret_cast<PIMAGE_SECTION_HEADER>(p);
    if((SectionH->PointerToRawData + SectionH->SizeOfRawData) > Result)
          Result = SectionH->PointerToRawData + SectionH->SizeOfRawData;
    p += sizeof(IMAGE_SECTION_HEADER);
  }
return Result;
}
//----------------------------------------------------------------------------
// change input file position
unsigned FSeek(const unsigned Offset,const WORD MoveMethod)
{ // We do not check for an error here, but if there is one FSeek and FilePos are $FFFFFFFF
  return SetFilePointer(VH_InFile, Offset, NULL, MoveMethod);
}
//----------------------------------------------------------------------------
// read from input file
unsigned FRead(void* Buffer,const unsigned Num)
{
  unsigned long dummy;
  return (ReadFile(VH_InFile, Buffer, Num, &dummy, NULL)) ? dummy : 0;
}
//----------------------------------------------------------------------------
// read a sfx header string
char* ReadSFXString(const int Len)
{
  char* ToRead = NULL;
  if(Len)
  {
    ToRead = new char[Len+1];
    FRead(ToRead, Len);
    ToRead[Len] = '\0';
  }
  return ToRead;
}
//----------------------------------------------------------------------------
// read a path from registry or return ''
char* GetPathFromRegistry(char* Path)
{
  // format hkxy\reg path\value[|suffix]
  char* Result = NULL;
  char Suffix[MAX_PATH + 2]; // max_path = 260
  char Value[MAX_PATH + 2];
  char Data[MAX_PATH + 2];
  Suffix[0] = Value[0] = Data[0] = '\0';
  char* p = strchr(Path,'\\');  // root\...
  if(p)
  {
    char Root[MAX_PATH + 2];
    strncpy(Root,Path,p - Path);
    Root[p-Path] = '\0'; // add terminationg 0
    strcpy(PathBuf, ++p);
    p = strchr(PathBuf, '|');  //  ...|suffix
    if(p)
    {
      strcpy(Suffix, p +1);
      *p = '\0';
    }
    p = strrchr(PathBuf, '\\');
    if(p) // ..\value
    {
      strcpy(Value, p +1);
      *p = '\0';
    }
    HKEY hkRoot, hkOpen;
    if(!strcmp(Root,"HKCU") || !strcmp(Root,"HKEY_CURRENT_USER")) hkRoot = HKEY_CURRENT_USER;
    else if(!strcmp(Root,"HKLM") || !strcmp(Root, "HKEY_LOCAL_MACHINE")) hkRoot = HKEY_LOCAL_MACHINE;
    else if(!strcmp(Root,"HKU") || !strcmp(Root, "HKEY_USERS")) hkRoot = HKEY_USERS;
    else hkRoot = 0;
    if(hkRoot && (RegOpenKey(hkRoot, PathBuf, &hkOpen) == ERROR_SUCCESS))
    {
      DWORD c = MAX_PATH;
      if(RegQueryValueEx(hkOpen, Value, NULL, NULL,
                        reinterpret_cast<BYTE*>(Data), &c) == ERROR_SUCCESS)
      {
        Data[c - 1] = '\0';  // assumed to be reg_sz or reg_expand_sz
        p = strchr(Data,';');
        if(p) *p ='\0';
        AppendDirSeparator(Data);
        Result = new char[strlen(Data) + strlen(Suffix) + 2];
        strcat(strcpy(Result, Data), Suffix);
      }
    }
    RegCloseKey(hkRoot);
  }
  delete[] Path;
  return Result;
}
//----------------------------------------------------------------------------
// ensure trailing backslash
void AppendDirSeparator(char* Dir)
{
  if(Dir[strlen(Dir) -1] != '\\') strcat(Dir,"\\");
}
//----------------------------------------------------------------------------
// ensure NO trailing backslash
char* RemoveDirSeparator(char* Dir)
{
  int i = strlen(Dir);
  if(Dir[i - 1] == '\\') Dir[i - 1] ='\0';
	return Dir;
}
//----------------------------------------------------------------------------
// expand environment variables
char* ExpandEnv(char* Str)
{
  if(!Str) return NULL;
  char* pch = new char[MAX_PATH * 2];
  if(ExpandEnvironmentStrings(Str, pch, (MAX_PATH *2) - 1) > 0)
  {
    delete[] Str;
    Str = new char[strlen(pch) + 2]; // need extra room for dir separator
    strcpy(Str,pch);
  }
  delete[] pch;
  return Str;
}
//----------------------------------------------------------------------------
void FormatStr(const char* Format,const char* arg, char* Dest)
{ // substitute the >< in Format by arg
  Dest[0] = '\0';
  unsigned i = 0;
  while(i <= strlen(Format))
  {
    if((Format[i] == '>') && (Format[i+1] == '<'))
    {
      strcat(Dest,arg);
      ++i;
    }
    else strncat(Dest,&Format[i],1);
    ++i;
  }
}
//----------------------------------------------------------------------------
void FormatStr2Arg(const char* Format, const char* arg1, const char* arg2, char* Dest)
{ // substitute the >< in Format by arg
  Dest[0] = '\0';
  unsigned i = 0;
  bool j = false;
  while(i <= strlen(Format))
  {
    if((Format[i] == '>') && (Format[i + 1] == '<'))
    {
      if(j) strcat(Dest,arg2);
      else
      {
        strcat(Dest,arg1);
        j = true;
      }
      ++i;
    }
    else strncat(Dest,&Format[i],1);
    ++i;
  }
}
//----------------------------------------------------------------------------
// get current directory
char* GetCurDir(void)
{
  char* Result = new char[MAX_PATH];
  GetCurrentDirectory(MAX_PATH, Result);
  return Result;
}
//----------------------------------------------------------------------------
// build a volatile path name (sfx_<unique name>)
char* GetUniqueVolatilePath(void)
{
  VBool_CheckDeleteVolatilePath = false;
  char* Result;
  char* temp = new char[MAX_PATH];
  temp = ExpandEnv(strcpy(temp,"%temp%"));
  AppendDirSeparator(temp);
  strcat(strcpy(PathBuf, temp),"sfx");
  char LStrDir[] = "0000";
  char tempbuf[7];
  for(int i = 0; i < 99999; ++i)  //  just 8 chars (dos conventions)
  {                               // "sfx + number"
    itoa(i, tempbuf, 10);
    LStrDir[4 - strlen(tempbuf)] = '\0';
    strcat(LStrDir, tempbuf);
    Result = new char[strlen(PathBuf) + strlen(LStrDir) + 4];
    strcat(strcpy(Result, PathBuf), LStrDir);
    if(!SfxFileExists(Result))
    {
      AppendDirSeparator(Result);
      VBool_CheckDeleteVolatilePath = true;
      VStr_VolatilePath = new char[strlen(Result) +1];
      strcpy(VStr_VolatilePath, Result);   // remember volatile path
      VStr_VolatilePath_Unexpanded = new char[strlen(LStrDir) + 4];
      strcpy(VStr_VolatilePath_Unexpanded, "sfx");
      strcat(VStr_VolatilePath_Unexpanded, LStrDir);
      break;
    }
    else delete[] Result;
  }
  delete[] temp;
  return Result;
}
//----------------------------------------------------------------------------
// expand path to include drive
char* ExpandPath(const char* Rel)
{
  char* p;
  char* Result = new char[MAX_PATH *2];
  GetFullPathName(Rel, MAX_PATH *2, Result, &p);
  return Result;
}
//----------------------------------------------------------------------------
void AddFilesListViewCol(const HWND wndDlg, const int Index,const char* Caption,
                         const int Direction, int Width)
{
  HWND wndLV = GetDlgItem(wndDlg, ID_LV_FILES);
  LV_COLUMN recCol;
  recCol.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
  recCol.fmt = Direction;
  recCol.cx = Width;
  recCol.pszText = const_cast<char*>(Caption);
  recCol.cchTextMax = strlen(Caption);
  recCol.iSubItem = Index;
  SendMessage(wndLV, LVM_INSERTCOLUMN, Index, reinterpret_cast<long>(&recCol));
}
//----------------------------------------------------------------------------
void GetRunCheckBoxText(char* Dest)
{
  GetRunString(Msg_RunCheckBox_Run, Msg_RunCheckBox_Inst, Dest);
}
//----------------------------------------------------------------------------
// resize dialog/control
void ResizeControl(const HWND wnd, const bool Reposition, int yDiff)
{
  WINDOWPLACEMENT pl;
  yDiff = MulDiv(yDiff, HIWORD(GetDialogBaseUnits()), 8);
  pl.length = sizeof(pl);
  GetWindowPlacement(wnd, &pl);
  if(Reposition) pl.rcNormalPosition.top = pl.rcNormalPosition.top + yDiff;
  pl.rcNormalPosition.bottom = pl.rcNormalPosition.bottom + yDiff;
  SetWindowPlacement(wnd, &pl);
}
//----------------------------------------------------------------------------
void FillListView(HWND wndOwner)
// fill the list view
{
  TZipEndOfCentral EOC;
  TZipCentralHeader CFH;
  unsigned CentralHeaderSig = ZipCentralHeaderSig;
  --CentralHeaderSig; //to get correct sig only in RAM
  char buffer[MAX_PATH *2];
  UINT EocPos = FSeek(FindEOCRecord(), FILE_BEGIN);
  FRead(&EOC, sizeof(EOC));
  short thisdisk = EOC.ThisDiskNo;
  VInt_SpanType = 0;
  //how far out the header offsets are from reality (due to sfx stub)
  VDW_OffsetDelta = EocPos - (EOC.CentralOffSet + EOC.CentralSize);
  FSeek(-static_cast<int>(sizeof(EOC)) - EOC.CentralSize, FILE_CURRENT);
  for(int i = 0; i < EOC.TotalEntries; ++i)
  {
    FRead(&CFH, sizeof(CFH));
    if(CFH.HeaderSig != CentralHeaderSig || CFH.FileNameLen == 0)
             ErrorHalt(Err_WrongFileSize, "");
    FRead(&buffer, CFH.FileNameLen);
    buffer[CFH.FileNameLen] = '\0';
    //swap slashes...
    for(int j = 0; j < CFH.FileNameLen; ++j)
      if(buffer[j] == '/') buffer[j] = '\\';

    // make oem filename conversion
    CheckCodePage(buffer, CFH);
     // skip directory entries
    if(buffer[CFH.FileNameLen - 1] != '\\')
    {
        // spanned/mv archive, get the type of spanning
        if(CFH.DiskStart != thisdisk && !VInt_SpanType && CFH.DiskStart)
                              CheckIfSpanned();
          //store each filename and absolute file offset of cfh record ...
        AddFileToList(wndOwner, buffer, &CFH);
    }
    FSeek(CFH.ExtraLen + CFH.FileComLen,FILE_CURRENT);
  }
}
//----------------------------------------------------------------------------
void SelectAllInFilesListView(const HWND wndDlg)
{
  LV_ITEM Item;
  Item.stateMask = LVIS_SELECTED;
  Item.state = LVIS_SELECTED;
  SendDlgItemMessage(wndDlg, ID_LV_FILES, LVM_SETITEMSTATE, -1, (long)&Item) ;
}
//----------------------------------------------------------------------------
// enable/disable all children of the given parent window
// this is used to disable all main dialog's controls during archive extraction
void EnableChildren(const HWND wnd, const bool Enable)
{
  EnumChildWindows(wnd, (WNDENUMPROC)FindChE, static_cast<int>(Enable));
}
//----------------------------------------------------------------------------
//call back for Enable Children
bool CALLBACK FindChE(HWND wnd, LPARAM lParam)
{
  char pCH[65];
  GetClassName(wnd, pCH, 63);
  if(IsWindowVisible(wnd) && strcmp(pCH,"msctls_progress32"))
      EnableWindow(wnd, static_cast<bool>(lParam));
  return true;
}
//----------------------------------------------------------------------------
//3.2.0.1 8 April 2004 RA use of TZipCentralHeader
bool Extract(HWND wndOwner)
{
  TZipCentralHeader* recCentral;
  HWND wndList = GetDlgItem(wndOwner, ID_LV_FILES);
  HWND wndProgressBar = GetDlgItem(wndOwner, ID_PRG_EXTRACT);
  long FileCount = SendMessage(wndList,LVM_GETITEMCOUNT,0,0);
  SendMessage(wndProgressBar, PBM_SETRANGE, 0, FileCount << 16);
  SendMessage(wndProgressBar, PBM_SETPOS, 0, 0);
  for(int i = 0; i < FileCount; ++i)
  {
    ProcessMessages();
    if(VBool_Cancelled) break;
    // update progres bar
    SendMessage(wndProgressBar, PBM_SETPOS, i+1, 0);
    if(SendMessage(wndList,LVM_GETITEMSTATE,i,LVIS_SELECTED) == LVIS_SELECTED) //if selected then...
    {
      //get the target filename...
      strcpy(PathBuf,VStr_ExtractPath);
      GetFileFromList(wndOwner, i, recCentral, TempBuf);
      delete[] VStr_CurrentFile;
      AppendDirSeparator(PathBuf);
      VStr_CurrentFile = new char[strlen(PathBuf) + strlen(TempBuf)+ 1];
      strcpy(VStr_CurrentFile, PathBuf);
      strcat(VStr_CurrentFile, TempBuf);
      ExtractFilePath(VStr_CurrentFile, PathBuf);
      if(!ForceDirectories(PathBuf))
      {
        FormatStr(Err_Directory, PathBuf, TempBuf);
        ErrorMsgBox(wndOwner, TempBuf);
        break;
      }
      if((VRec_SFXHeader.DefOVW != somOverwrite) && SfxFileExists(VStr_CurrentFile))
      {
        if(VRec_SFXHeader.DefOVW == somSkip) continue;
        switch(DialogBox(MInstance, Str_Dlg_FileExists, wndOwner ,
                         reinterpret_cast<DLGPROC>(FileExistsDialogProc)))
        {
          case ID_BTN_YES: break;
          case ID_BTN_NO: continue;
        }
      }
      bool PWFailed = false;
      bool Extracted;
         // make sure the correct zip archive is open
      if((VRec_SFXHeader.Options & soDetached) || VInt_SpanType)
            OpenRightArchive(wndOwner, recCentral->DiskStart);
      Extracted = ExtractFile(wndOwner, VStr_CurrentFile, recCentral, PWFailed);
      if(PWFailed) break; //stop further processing!!!
      else if(Extracted)
        //unselect the file if successfully extracted with no errors...
        DeSelectInFilesListView(wndOwner, i);
    }
  }
  if(!(SendMessage(wndList ,LVM_GETSELECTEDCOUNT ,0,0) == 0))
  {
    ShowFirstSelected(wndList);
    return false;
  }
  else return true;
}
//----------------------------------------------------------------------------
unsigned ExecuteCMD(void)
{
  unsigned Result = 0;
  if(VRec_SFXHeader.CmdLineSize > 0)
  {
    GetArgument(1, PathBuf);
    GetArgument(2, TempBuf);
    char* srOld = GetCurDir();
    if(strlen(VStr_ExtractPath)) chdir(VStr_ExtractPath);
    if(strlen(PathBuf) > 4)
    {
      if(TestForInf(PathBuf)) Result = ExecInf(PathBuf); //error if < 32
      else Result = reinterpret_cast<int>(ShellExecute(0, "open", PathBuf, TempBuf,
                                                       VStr_ExtractPath, SW_SHOW));
    }
    chdir(srOld);
    delete[] srOld;
  }
  if((Result >= 32) && VBool_CheckDeleteVolatilePath &&
     CompareText(RemoveDirSeparator(VStr_VolatilePath),RemoveDirSeparator(VStr_ExtractPath)) &&
     DirectoryExists(RemoveDirSeparator(VStr_VolatilePath)))
    RemoveDirEx();
  return Result;
}
//----------------------------------------------------------------------------
// get an error message if ExcuteCMD failed
void GetRunErrorMessage(char* Dest)
{
  GetRunString(Err_Run_Run, Err_Run_Inst, Dest);
}
//----------------------------------------------------------------------------
void GetRunString(const char* Run, const char* Inst, char* Dest)
{
  char* temp = new char[MAX_PATH];
  char* filename =  new char[MAX_PATH];
  char* arg2 =  new char[MAX_PATH];
  GetArgument(1, temp);
  ExtractFileName(temp, filename);
  if(TestForInf(filename))
  {
   GetArgument(2, temp);
   ExtractFileName(temp, arg2);
   FormatStr2Arg(Inst, filename , arg2, Dest);
  }
  else
  {
    if(!strlen(filename))
    {
      GetArgument(1, temp);
      if(!strlen(temp))
      {
        RemoveDirSeparator(temp);
        ExtractFileName(temp, filename);
      }
    }
    GetArgument(2, temp);
    ExtractFileName(temp, arg2);
    FormatStr2Arg(Run, filename , arg2, Dest);
  }
  char* p = Dest;
  while(*p == ' ') ++p;
  memmove(Dest, p, strlen(p));
  p = Dest + strlen(Dest);
  while(*p == ' ') --p;
  *p = '\0';
  delete[] temp;
  delete[] filename;
  delete[] arg2;
}
//----------------------------------------------------------------------------
// to check correct file size of the input file +++ 08/13/98
unsigned FindEOCRecord(void)
{
  TZipEndOfCentral* Rec;
  unsigned Result = -1;
  unsigned EndOfCentralSig = ZipEndOfCentralSig;
  --EndOfCentralSig; // correct sig only in RAM
  // get the needed size of the buffer
  bool OK = false;
  UINT BufferSize = GetFileSize(VH_InFile, NULL) - static_cast<UINT>(VInt_FileBegin);
  if(BufferSize > 65558) BufferSize = 65558;
  if(BufferSize > sizeof(TZipEndOfCentral))
  {//if smaller, then no correct zip file
    char* Buffer = new char[BufferSize];
    try
    {
      UINT CurrentPos = FSeek(0, FILE_CURRENT);
      UINT FilePos = FSeek(-(static_cast<int>(BufferSize)), FILE_END);
      UINT Read = FRead(Buffer, BufferSize);
      FSeek(CurrentPos, FILE_BEGIN);
      if(Read == BufferSize)
        for(UINT c = 0; c <= (BufferSize - sizeof(TZipEndOfCentral)); ++c)
        {
          Rec = reinterpret_cast<TZipEndOfCentral*>(Buffer + c);
          if(Rec->HeaderSig == EndOfCentralSig)
          {
            // eocd is found, now check if size is correct ( = pos+22+eocd.commentsize)
            if((Rec->ZipCommentLen + FilePos + c + sizeof(TZipEndOfCentral)) ==
                       GetFileSize(VH_InFile, NULL))
            {
              OK = true; // set ok flag
              Result = FilePos + c;
              break;
            }
          }
        }
    }
    __finally
    {
      delete[] Buffer;
    }
  }
  if(!OK) ErrorHalt(Err_WrongFileSize, "");
  return Result;
}
//----------------------------------------------------------------------------
// may 09, 2002: check the codepage of a central file record and eventually convert to ansi
void CheckCodePage(char* In, const TZipCentralHeader recHdr)
{
  char* Dest = new char[strlen(In) + 1];
  if((recHdr.VersionMadeBy1 == 0) || (recHdr.VersionMadeBy1 == 6) ||
     (recHdr.VersionMadeBy1 == 11) && (recHdr.VersionMadeBy0 == 50))
  {
    OemToChar(In, Dest);
    strcpy(In, Dest);
  }
  delete[] Dest;
}
//----------------------------------------------------------------------------
//3.2.0.1 8 April 2004 RA use of CentralHeaders array added
// add an entry to the list view
void AddFileToList(const HWND wndOwner, char* Name, const TZipCentralHeader* Rec)
{
  LV_ITEM recItem;
  SHFILEINFO sfi;
  HWND wndLV = GetDlgItem(wndOwner, ID_LV_FILES);
  SHGetFileInfo( Name, FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(sfi),
               SHGFI_USEFILEATTRIBUTES | SHGFI_SMALLICON | SHGFI_SYSICONINDEX);
  recItem.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
  recItem.iItem = MaxInt;
  recItem.iSubItem = 0;
    // select only if no file selection is allowed (just for visual feedback)
  if(!(VRec_SFXHeader.Options & soAskFiles)) recItem.state = 0;
  else recItem.state = LVIS_SELECTED;
  recItem.stateMask = LVIS_SELECTED;
  recItem.pszText = Name;
  recItem.cchTextMax = strlen(Name);
  recItem.iImage = sfi.iIcon;
  recItem.lParam = AddToItemData(Rec);
  int iiItem = SendMessage(wndLV, LVM_INSERTITEM ,0, reinterpret_cast<long>(&recItem));
  recItem.mask = LVIF_TEXT;
  recItem.iItem = iiItem;
  recItem.iSubItem = 1;
  char buf[35];
  itoa(Rec->UnComprSize,buf, 10);
  recItem.pszText = buf;
  recItem.cchTextMax = strlen(buf);
  SendMessage(wndLV, LVM_SETITEM, 0, reinterpret_cast<long>(&recItem));
}
//----------------------------------------------------------------------------
void ProcessMessages(void)
{ // messqge structure is made global got stack errors
  PeekMessage(&Msg, 0, 0, 0, PM_REMOVE);
  TranslateMessage(&Msg);
  DispatchMessage(&Msg);
}
//----------------------------------------------------------------------------
// retrieve an item from the list
//3.2.0.1 8 April 2004 RA Signature changed
void GetFileFromList(HWND wndOwner, const int iiItem, TZipCentralHeader*& Rec,char* Dest)
{
 LV_ITEM Item;
 char Buf[MAX_PATH* 2];
 HWND wndLV = GetDlgItem(wndOwner, ID_LV_FILES);
 // get lparam stored in routine above
 Item.iItem = iiItem;
 Item.iSubItem = 0;
 Item.mask = LVIF_PARAM;
 SendMessage(wndLV, LVM_GETITEM, 0, reinterpret_cast<long>(&Item));
 Rec = GetFromItemData(Item.lParam);
  // path+file
 Item.iItem = iiItem;
 Item.iSubItem = 0;
 Item.mask = LVIF_TEXT;
 Item.pszText = Buf;
 Item.cchTextMax = sizeof(Buf);
 SendMessage(wndLV, LVM_GETITEMTEXT, iiItem, reinterpret_cast<long>(&Item));
 strcpy(Dest, Item.pszText);
}
//----------------------------------------------------------------------------
// force the existence of a directory (and its parents)
bool ForceDirectories(char* Dir)
{
  bool Result;
  RemoveDirSeparator(Dir);
  if(!strlen(Dir)) return true;
  char* temp = new char[strlen(Dir) + 1];
  ExtractFilePath(Dir, temp);
  if(DirectoryExists(Dir) || !strcmp(Dir, temp)) Result = true; // avoid 'xyz:\' problem.
  else
  {
    if(!ForceDirectories(temp)) Result = false;
    else Result = CreateDirectory(Dir,NULL);
  }
  delete[] temp;
  return Result;
}
//----------------------------------------------------------------------------
bool ExtractFile(HWND wndOwner,char* Filename, const TZipCentralHeader* rec,bool& PasswordFailed)
{
  unsigned LocalHeaderSig = ZipLocalHeaderSig;
  unsigned CentralHeaderSig = ZipCentralHeaderSig;
  --LocalHeaderSig; //correct sig only in RAM
  --CentralHeaderSig; //correct sig only in RAM
  TZipLocalHeader rLocal;
  HANDLE oldH;
  bool Result = false;
  bool IsTempFile = false;
  VRec_ZipHeader = *rec;
  if(VRec_ZipHeader.HeaderSig != CentralHeaderSig)
      ErrorHalt(Err_Archive ,VStr_ExeName);
  else
  {
    if((VRec_ZipHeader.ComprMethod > 8) || (static_cast<int>(VRec_ZipHeader.ComprMethod) < 0))
      ErrorMsgBox(wndOwner, Err_ZipUnknownComp);
    else
    {
      unsigned Offset = VRec_ZipHeader.RelOffLocal + VDW_OffsetDelta;
      if((VRec_SFXHeader.Options & soDetached) || VInt_SpanType)
      {
        // assure the right disk is opened
        OpenRightArchive(wndOwner, VRec_ZipHeader.DiskStart);
         // join the possibly splitted data in a temporary file
        ExtractToTempFile(wndOwner, Offset, oldH);
        IsTempFile = true;
      }
      try
      {
        //goto beginning of local header...
        FSeek(Offset, FILE_BEGIN);
        FRead(&rLocal, sizeof(rLocal));
        if(rLocal.HeaderSig != LocalHeaderSig)
            ErrorHalt(Err_Archive,VStr_ExeName);
        // added extrafiledlen to go to correct position
        // e.g. for zipfiles created with infozip's zip.exe
        FSeek(VRec_ZipHeader.FileNameLen + rLocal.ExtraLen, FILE_CURRENT);
        VInt_BytesToGo = VRec_ZipHeader.ComprSize;
        //password stuff...
        if(VRec_ZipHeader.Flag & 1) //if a password used...
        {
          VInt_BytesToGo -= RAND_HEAD_LEN;
          char* EncryptHdr = new char[RAND_HEAD_LEN * 2];
          try
          {
            FRead(&EncryptHdr[0], RAND_HEAD_LEN);
            //make a copy of encrypted header in upper half of buffer...
            memcpy(&EncryptHdr[RAND_HEAD_LEN], &EncryptHdr[0], RAND_HEAD_LEN);
            if(!VStr_Password) PasswordFailed = true;
            else PasswordFailed = !(decrypt_pw(EncryptHdr, RAND_HEAD_LEN, VRec_ZipHeader.Flag,
                                                VRec_ZipHeader.CRC32, VRec_ZipHeader.ModifTime, VStr_Password));
            if(PasswordFailed)
            {
              for(int i = 0; i < 3; ++i)
              {
                if(DialogBox(MInstance, Str_Dlg_Password, wndOwner,
                             reinterpret_cast<DLGPROC>(PasswordQueryDialogProc)) != IDOK) break;
                PasswordFailed = !(decrypt_pw(EncryptHdr, RAND_HEAD_LEN, VRec_ZipHeader.Flag, VRec_ZipHeader.CRC32,
                                              VRec_ZipHeader.ModifTime, VStr_Password));
                if(!PasswordFailed) break;
                MessageBeep(MB_ICONHAND	); //it's a dud, ? try again...
              }
            }
          }
          __finally
          {
            delete[] EncryptHdr;
          }
        }
        if(PasswordFailed) return Result;
        VDW_CRC32Val = CRC_MASK;
        ProcessMessages();
        if(VBool_Cancelled) return Result;
        if((VH_OutFile = CreateFile(Filename, GENERIC_WRITE, FILE_SHARE_WRITE,
                NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)) == INVALID_HANDLE_VALUE)
        {
          FormatStr(Err_CannotOpenFile, Filename,TempBuf);
          ErrorMsgBox(wndOwner, TempBuf);
          return Result;
        }
        try
        {
          if(VRec_ZipHeader.ComprMethod == 0) Unstore();
          else Inflate();
          FileSetDate(VH_OutFile, VRec_ZipHeader.ModifDate, VRec_ZipHeader.ModifTime);
            // 01/13/04: do crc32 checking, bail a warning message but do not stop if checksums do not match

          if(rec->CRC32 != (VDW_CRC32Val ^ 0xFFFFFFFF))
          {
            FormatStr(Err_CRC32, Filename, TempBuf);
            ErrorMsgBox(wndOwner, TempBuf);
            Result = false;
          }
          else Result = true;
        }
        __finally
        {
          if(!CloseHandle(VH_OutFile))
          {
            FormatStr(Err_CannotCloseFile, Filename, TempBuf);
            ErrorMsgBox(wndOwner,TempBuf);
            Result = false;
          }
        }
      }
      __finally
      {
        if(IsTempFile) RollBackTempFile(wndOwner, oldH);
      }
    }
  }
  return Result;
}
//----------------------------------------------------------------------------
void DeSelectInFilesListView(const HWND wndDlg, const int Item)
{
  LV_ITEM LVItem;
  LVItem.stateMask = LVIS_SELECTED;
  LVItem.state = 0;
  SendDlgItemMessage(wndDlg, ID_LV_FILES, LVM_SETITEMSTATE, Item, reinterpret_cast<long>(&LVItem));
}
//----------------------------------------------------------------------------
// may 11, 2002: show first selected item on extraction failure
void ShowFirstSelected(const HWND wndList)
{
  for(int i = 0; i < SendMessage(wndList,LVM_GETITEMCOUNT ,0,0);++i)
  {
    if(SendMessage(wndList, LVM_GETITEMSTATE, i, LVIS_SELECTED) == LVIS_SELECTED)
    {
      SendMessage(wndList, LVM_ENSUREVISIBLE, i, 0);
      break;
    }
  }
}
//----------------------------------------------------------------------------
// gets an argument from the stored command line
//                1 : the part before the pipe (if there's no pipe, returns the whole command line)
//                2 : the part after the pipe (if no pipe, returns "")
//                all "><" will be replaced by the extraction path
void GetArgument(const int Index, char* Dest)
{
  AppendDirSeparator(VStr_ExtractPath);
  strcpy(Dest,VStr_SFX_CmdLine);
  char* pip = strchr(Dest,'|');
  if(!pip)
  {
    if(Index == 2) strcpy(Dest,"");
  }
  else
  {
    if(Index == 1)
    {
      *pip = '\0';
      strcat(Dest, " ");  // add separator between arguments
    }
    else memmove(Dest, ++pip, strlen(pip)); // makes it better readable
  }
  while(strstr(Dest,"><"))
  {
   char temp[MAX_PATH];
   FormatStr(Dest, VStr_ExtractPath, temp);
   strcpy(Dest, temp);
  }
  // get the short (8+3)-filename (it seems that shellexecute has some problems with lfn)
  GetShortPathName(Dest,Dest, strlen(Dest));
}
//----------------------------------------------------------------------------
bool TestForInf(char* sr1)
{
  char* p;
  while((p = strrchr(sr1 , ' ')) != NULL) *p = '\0'; // remove traling spaces
  return CompareText(".inf", &sr1[strlen(sr1) -4 ]);
}
//----------------------------------------------------------------------------
//execute inf-scripts using rundll
unsigned ExecInf(char* Path)
{
  char temp[MAX_PATH];
  strcat(strcpy(temp, "rundll32.exe setupapi.dll,InstallHinfSection DefaultInstall 132 "), Path);
  return WinExec(temp, SW_SHOW);
}
//----------------------------------------------------------------------------
// does the directory exist?
bool DirectoryExists(const char* Dir)
{
  if(!Dir) return false;
	DWORD Code = ::GetFileAttributes(Dir);
	return (Code != 0xFFFFFFFF) & ((FILE_ATTRIBUTE_DIRECTORY & Code) != 0);
}
//----------------------------------------------------------------------------
// remove extract directory next time a user logs on
void RemoveDirEx(void)
{
  char LStrCmd[MAX_PATH *2];
  if(IsWinNT())
  {
    // the following does not work with all types of drives
    strcat(strcat(strcpy(LStrCmd,"cmd.exe /c @rd /s /q \"%temp%\\" ),
                         VStr_VolatilePath_Unexpanded), "\">nul 2>nul");
  }
  else
  {
    strcat(strcat( strcpy(LStrCmd,"command.com /c deltree /y \"%temp%\\"),
            VStr_VolatilePath_Unexpanded), "\">nul");
  }
  bool LBoolSuccess;
  HKEY LHKSub;
  if(RegCreateKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", &LHKSub)
          == ERROR_SUCCESS)
  {
    LBoolSuccess = RegSetValueEx(LHKSub, VStr_VolatilePath_Unexpanded, 0, REG_SZ,
                   reinterpret_cast<BYTE*>(LStrCmd), strlen(LStrCmd)+1) == ERROR_SUCCESS;
    RegCloseKey(LHKSub);
  }
  else LBoolSuccess = false;
  // try current user if not successfull
  if(!LBoolSuccess && RegCreateKey(HKEY_CURRENT_USER,
                                    "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce",
                                    &LHKSub) == ERROR_SUCCESS)
  {
    RegSetValueEx(LHKSub, VStr_VolatilePath_Unexpanded, 0, REG_SZ,
                  reinterpret_cast<BYTE*>(LStrCmd), strlen(LStrCmd)+1);
    RegCloseKey(LHKSub);
  }
}
//----------------------------------------------------------------------------
bool decrypt_pw(char* Encrypt_Head, BYTE EncHead_len, WORD BitFlag, long CRC,
                long FileDate, char* Password)
{
  bool Result = false;
  if(!strlen(Password)) return false;
  seedk(Password);
  for(int i = 0; i < EncHead_len; ++i)
  {
    byte c = static_cast<byte>((Encrypt_Head[i + EncHead_len]) ^ decrypt_byte());
    UpdateKeys(c);
    Encrypt_Head[i] = static_cast<char>(c);
  }
/*
  // this version checks 2 bytes 16 bits
  WORD w = Encrypt_Head[EncHead_len - 2] | (Encrypt_Head[EncHead_len - 1] << 8);
  if(!((BitFlag & 8) == 8))
  {
    if(w == HIWORD(CRC)) Result = true;
  }
  else
  {
    if(w == LOWORD(FileDate)) Result = true;
  }
*/
// version that check 1 byte 8 bits
  byte b = static_cast<byte>(Encrypt_Head[EncHead_len - 1]);
  if(!((BitFlag & 8) == 8))
  {
    if(b == HIBYTE(HIWORD(CRC))) Result = true;
  }
  else
  {
    if(b == (LOWORD(FileDate) >> 8)) Result = true;
  }

  return Result;
}
/* Note on password checking:
A 2 byte (16 bits) version is more accurate in password checking but makes brute
force attempts to break the password faster, hence easier.
The 1 byte version is a compromise between the risk of accepting a false password
as correct and faster response to breaking attempts
*/
//----------------------------------------------------------------------------
// unstore the current archive file / uncompressed
void Unstore(void)
{
  ULONG NumBytes;
  char* OutBuf = new char[min(VInt_BytesToGo, WSIZE) + 2];
  try
  {
     while(VInt_BytesToGo > 0)
     {
      NumBytes = FRead(OutBuf, min(VInt_BytesToGo, WSIZE));
      VInt_BytesToGo -= NumBytes;
      if((VRec_ZipHeader.Flag & 1) == 1)
      {
        for(ULONG c = 0; c < NumBytes; ++c)
        {
          OutBuf[c] = static_cast<char>(static_cast<byte>(OutBuf[c]) ^ decrypt_byte());
          UpdateKeys(static_cast<byte>(OutBuf[c]));
        }
      }
      WriteFile(VH_OutFile, OutBuf, NumBytes, &NumBytes, NULL);
      Crc32_Buf(OutBuf, NumBytes, VDW_CRC32Val);
     }
  }
  __finally
  {
    delete[] OutBuf;
  }
}
//----------------------------------------------------------------------------
// get operating system type (nt/win)
bool IsWinNT(void)
{
  OSVERSIONINFO osvi;
  osvi.dwOSVersionInfoSize = sizeof(osvi);
  return (GetVersionEx(&osvi) && (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT));
}
//----------------------------------------------------------------------------
// Initialize the encryption keys and the random header according to the given password.
void seedk(char* passwd)
{
  VArr_CryptKey[0] = 305419896L;
  VArr_CryptKey[1] = 591751049L;
  VArr_CryptKey[2] = 878082192L;
  while (*passwd != '\0')
  {
    UpdateKeys (static_cast<byte>(*passwd));
    passwd++;
  }
}
//----------------------------------------------------------------------------
// Return the next byte in the pseudo-random sequence
int decrypt_byte(void)
{
  unsigned temp = (static_cast<unsigned>( VArr_CryptKey[2] & 0xffff)) | 2;
  return static_cast<int>(((temp * (temp ^ 1)) >> 8) & 0xff);
}
//----------------------------------------------------------------------------
// Update the encryption keys with the next byte of plain text
void UpdateKeys(byte c)
{
  VArr_CryptKey[0] = UpdC32(VArr_CryptKey[0], c);
  VArr_CryptKey[1] += VArr_CryptKey[0] & 0xff;
  VArr_CryptKey[1] = VArr_CryptKey[1] * 134775813L + 1;
  {
    register int keyshift = (int) (VArr_CryptKey[1] >> 24);
    VArr_CryptKey[2] = UpdC32(VArr_CryptKey[2], static_cast<char>(keyshift));
  }
}
//----------------------------------------------------------------------------
// fill crc32 buffer
void Crc32_Buf(char* str, int len, unsigned& crc)
{
  while(len > 0)
  {
    crc = UpdC32(crc, static_cast<byte>(*str));
    ++str;
    --len;
  }
}
//----------------------------------------------------------------------------
unsigned UpdC32(unsigned Crc, byte Chr)
{
return VArr_CRC32Table[(static_cast<int>(Crc) ^ Chr) & 0xff] ^ ((Crc >> 8) & 0x00FFFFFF);
}
//----------------------------------------------------------------------------
// does the file exist?
bool SfxFileExists(const char* FileName)
{
  if(!FileName) return false;
  DWORD Code = ::GetFileAttributes(FileName);
	return (Code != 0xFFFFFFFF);
}
//----------------------------------------------------------------------------
// compare two strings / case insensitive
bool CompareText(char* s1, char* s2)
{
  return (strcmpi(s1, s2) == 0);
}
//----------------------------------------------------------------------------
// handle relative pathes, strip directory name
void ExtractFileName(char* FileName, char* Dest)
{
 char* p = strrchr(FileName,'\\');
 if(!p) p = strrchr(FileName,'/');
 if(!p) p = strrchr(FileName,':');
  // Handle archive relative paths
 if(p) strcpy(Dest,++p);
 else strcpy(Dest,FileName);
}
//----------------------------------------------------------------------------
// extract the file's path
void ExtractFilePath(char* Filename, char* Dest)
{
  strcpy(Dest, Filename);
  unsigned i = strlen(Filename);
  if((i ==3) && strchr(Filename,':')) return;
  char* p = strrchr(Dest, '\\');
  if(!p) p = strrchr(Dest, '/');
  if(!p) p = strrchr(Dest, ':');
  if((*p == '\\') || (*p == '/'))
  {
    i = p - Dest;
    if(i != 3) ++p;
    else if(Dest[2] != ':') ++p;// avoid 'xyz:\' problem.
  }
  *p = '\0';
}
//----------------------------------------------------------------------------
void FileSetDate(const HANDLE Handle, const unsigned short Date, const unsigned short Time)
{
  FILETIME LocalFileTime;
  FILETIME FileTime;
  DosDateTimeToFileTime(Date, Time, &LocalFileTime);
  LocalFileTimeToFileTime(&LocalFileTime, &FileTime);
  SetFileTime(Handle, NULL, NULL, &FileTime);
}
//----------------------------------------------------------------------------
//3.2.0.1 8 April 2004 RA new function
TZipCentralHeader* GetFromItemData(const int Index)
{
  return CentralHeaders[Index -1];
}
//----------------------------------------------------------------------------
unsigned AddToItemData(const TZipCentralHeader* rec)
{
  TZipCentralHeader* NRec = new TZipCentralHeader;
  memcpy(NRec, rec, sizeof(TZipCentralHeader));
  CentralHeaders[CentralHeadersIndex++] = NRec;
  return CentralHeadersIndex;
}
//----------------------------------------------------------------------------
void SfxExit(bool success, bool doexit)
{
  delete[] VStr_ExeName;
  delete[] VStr_SFX_Caption;
  delete[] VStr_SFX_StartMsg;
  delete[] VStr_SFX_CmdLine;
  delete[] VStr_SFX_RegFailPath;
  delete[] VStr_SFX_Path;
  delete[] VStr_VolatilePath;
  delete[] VStr_VolatilePath_Unexpanded;
  delete[] VStr_Password;
  delete[] VStr_ExtractPath;
  delete[] VStr_CurrentFile;

  delete[] VStr_DetachName;
  delete[] VStr_DetachExt;
  delete[] VStr_SourceDir;
  for(int i = 0; i < CentralHeadersIndex; ++i)
  {
    TZipCentralHeader* rec = CentralHeaders[i];
    delete rec; // release all central headers
  }
  Release_Fixed_Tables(); // release inflate fixed tables
  if(doexit)
  {
    if(success) exit(EXIT_SUCCESS);
    else exit(EXIT_FAILURE);
  }
}



