Subversion Repositories autosfx

Rev

Blame | Last modification | View Log | RSS feed

  1.  (******************************************************************)
  2.  (* SFX for DelZip v1.8                                            *)
  3.  (* Copyright 1997, Microchip Systems / Carl Bunton                *)
  4.  (* e-mail: Twojags@cris.com                                       *)
  5.  (* Web-page: http://www.concentric.net/~twojags                   *)
  6.  (*                                                                *)
  7.  (* modified by Markus Stephany                                    *)
  8. (* modified by Russell Peters, Roger Aelbrecht
  9.   This library is free software; you can redistribute it and/or
  10.   modify it under the terms of the GNU Lesser General Public
  11.   License as published by the Free Software Foundation; either
  12.   version 2.1 of the License, or (at your option) any later version.
  13.  
  14.   This library is distributed in the hope that it will be useful,
  15.   but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17.   Lesser General Public License (licence.txt) for more details.
  18.  
  19.   You should have received a copy of the GNU Lesser General Public
  20.   License along with this library; if not, write to the Free Software
  21.   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  22.  
  23.   contact: problems AT delphizip DOT org
  24.   updates: http://www.delphizip.org
  25.  
  26.   modified 2008-11-03
  27. ---------------------------------------------------------------------------*)
  28. unit ZMSFXProcs19;
  29.  
  30. {
  31. this unit contains utility functions and main function used by delzipsfx
  32.  
  33. }
  34.  
  35. interface
  36.  
  37. { modifications marked with ##FR are enhancements and bug fixes by
  38. Frank Reichert F.Rei@gmx.de, thanks!
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45. !!!!!!!!!!!!! spanning/multivolume support based on Roger Aelbrecht's BCB
  46. version of the sfx; thanks a lot Roger! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  47.  
  48.  
  49. }
  50.  
  51. uses Messages, Windows, ZMSFXDefs19, ZMSFXInt19, ZMSFXStrings19,
  52. {$ifdef DEBUG_SFX}
  53.    SysUtils,  // Run1,
  54. {$endif}
  55.   ZMSFXStructs19;
  56.  
  57. // execute  
  58. procedure Run;  
  59.  
  60.  // resize or move a control on the main dialog (or the dialog itself)
  61.  // depending on the visibility of the files listview
  62. procedure ResizeControl(const wnd: HWND; const bReposition: boolean; yDiff: integer);
  63.  
  64.  // enable/disable all child controls of the given window
  65.  // except of progress bars
  66. procedure EnableChildren(const wnd: HWND; const bEnable: boolean);
  67.  
  68. // get the text for the run checkbox
  69. function GetRunCheckBoxText: string;
  70.  
  71. // get an error message if ExcuteCMD failed
  72. function GetRunErrorMessage: string;
  73.  
  74. // compare two strings / case insesitive
  75. function CompareText(const s1, s2: string): boolean;
  76.  
  77. // get an argument out of VStr_SFX_CmdLine
  78. function GetArgument(const iIndex: integer): string;
  79.  
  80. // check whether the file to execute is an .inf installation file
  81. function TestForInf(const sr1: string): boolean;
  82.  
  83. // format a string (replace '><' by args)
  84. function FmtStr1(const sFormat: string; const arg1: string): string;
  85. function FmtStrID1(id: integer; const arg1: string): string;
  86. function FmtStr2(const sFormat: string; const arg1, arg2: string): string;
  87. //function FmtStr3(const sFormat: string; const arg1, arg2, arg3: string): string;
  88. //function FmtStrID2(id: integer; const arg1, arg2: string): string;
  89.  
  90.  // angus johnson, ajohnson@rpi.net.au
  91.  // set the filetime of an extracted file to the value stored in the archive
  92. procedure FileSetDate(const hFile: THandle; const iAge: integer);
  93.  
  94. // unstore the current archive file / uncompressed
  95. procedure Unstore;
  96.  
  97. // force the existence of a directory (and its parents)
  98. function ForceDirectories(sDir: string): boolean; // RCV04
  99.  
  100. // change input file position
  101. function FSeek(const Offset: int64; const MoveMethod: word): int64; //##FR was procedure
  102.  
  103. // fill crc32 buffer
  104. procedure Crc32_Buf(str: PByte; len: integer; var crc: cardinal);
  105.  
  106. // handle relative paths, strip directory name
  107. function ExtractFileName(const sFileName: string): string;
  108.  
  109. // return the smaller value
  110. function Min(const I1, I2: longint): longint;
  111.  
  112. // ensure trailing backslash
  113. function AppendDirSeparator(const sDir: string): string; //##FR modified
  114.  
  115. // ensure NO trailing backslash
  116. function RemoveDirSeparator(const sDir: string): string;
  117.  
  118. // does the directory exist?
  119. function DirectoryExists(const sDir: string): boolean;
  120.  
  121. // does the file exist?
  122. function FileExists(const sFileName: string): boolean;
  123.  
  124. // extract the file's path
  125. function ExtractFilePath(const sFilename: string): string;
  126.  
  127. // replace environment vars by their contents
  128. function ExpandEnv(const Str: string): string;
  129.  
  130. // show a message box
  131. function MsgBox(const wndpar: HWND; const sMsg, sTitle: string;
  132.   const uType: cardinal): integer;
  133.  
  134. // show an error message
  135. procedure ErrorMsgBox(const wndPar: HWND; const sMsg: string);
  136. procedure ErrorMsgBoxFmt1(const wndPar: HWND; id: integer; const arg1: string);
  137.  
  138. // read from input file
  139. function FRead(var Buffer; const cNum: cardinal): cardinal;
  140.  
  141. // read from a file and bail if not all data could be read
  142. procedure CheckFRead(var Buffer; const cNumBytes: cardinal);
  143.  
  144. // write to a file and bail if not all data could be written
  145. procedure CheckFWrite(const FH: THandle; const Buffer; const cNumBytes: cardinal;
  146.   const FileName: string);
  147.  
  148.  
  149. // read TSFXFileHeader from input file
  150. procedure GetDefParams;
  151.  
  152. // execute the command-line read from the sfx header, if any
  153. function ExecuteCMD: cardinal;
  154.  
  155. // check password
  156. function decrypt_pw(Encrypt_Head: PAnsiChar; EncHead_len: byte; BitFlag: word;
  157.     CRC, FileDate: longint; const sPassword: AnsiString): boolean;
  158.  
  159. // decrypt arcive contents
  160. function decrypt_byte: integer;
  161.  
  162. // Update the encryption keys with the next byte of plain text
  163. procedure UpdateKeys(c: byte);
  164.  
  165. // initially fill the crc table
  166. procedure Make_CRC32Table;
  167.  
  168.  // from Angus Johnson's TZip-SFX code:
  169.  // get the executable's file size to get rid of caring about the exe size
  170. function GetExeSize: cardinal;
  171.  
  172.  // from Angus Johnson's TZip-SFX code:
  173.  // fill the listview
  174. procedure FillListView(wndOwner: hWnd);
  175.  
  176. // fatal error, exit                      
  177. procedure ErrorHaltID(id: integer); overload;
  178. procedure ErrorHaltFmt(id: integer; const arg1: string);
  179. procedure ErrorHalt(const sMsg: string);
  180.  
  181. function Extract(wndOwner: hWnd): boolean;
  182.  
  183. function StrGetEditText(wndPar: HWND): string;
  184.  
  185.  
  186. // listview handling
  187. procedure AddFilesListViewCol(const wndDlg: HWND; const iIndex: integer;
  188.   const szCaption: string; const iDirection, iWidth: integer);
  189.  
  190. procedure SelectAllInFilesListView(const wndDlg: HWND);
  191.  
  192. // get current directory
  193. function GetCurDir: string;
  194.  
  195. // add an entry to the list view
  196. procedure AddFileToList(const wndOwner: HWND; const sName: string;
  197.   const Rec: TZ64CentralEntry; const IsDir: boolean);
  198. //  const Rec: TZipCentralHeader; const IsDir: boolean);
  199.  
  200. procedure SetLangStrings(hLC: hWnd);
  201.  
  202. // return pointer to temporary buffer of at least size
  203. function GetXBuf(size: integer): pByte;
  204.  
  205. // dispatch windows messages
  206. procedure ProcessMessages;
  207.  
  208. // Int to Str
  209. function Int2Str(n: int64; wide: integer = -1): String;
  210.  
  211. // return the Detached name
  212. function DetachedName(const num: string): string;
  213.  
  214. function LoadResource(id: integer): Pointer;
  215.  
  216. // cleanup, free globals
  217. procedure Finish;
  218. //{$endif}
  219.  
  220. // check codepage of filename
  221. implementation
  222.  
  223. uses
  224.   ZMSFXDialogs19, ZMSFXVars19, ZMSFXInflate19, ZMSFXWinTrust;//, SysUtils;
  225.  
  226. var
  227.   xbuf: PByte = nil;
  228.   xbufsize: integer = 0;
  229.  
  230. // return pointer to temporary buffer of at least size
  231. function GetXBuf(size: integer): pByte;
  232. begin
  233.   if (xbuf <> nil) and (xbufsize > 0) and (size <= xbufsize) then
  234.   begin
  235.     // use the existing buffer
  236.     Result := pByte(xbuf);
  237.     Exit;
  238.   end;
  239.  
  240.   if (size > xbufsize) or (size < 0) or (xbuf = nil) or (xbufsize <= 0) then
  241.   begin
  242.     // clear old buf
  243.     if xbuf <> nil then
  244.       FreeMem(xbuf);
  245.     xbuf := nil;
  246.     xbufsize := 0;
  247.     Result := nil;
  248.     if size <= 0 then
  249.       exit;
  250.   end;
  251.   xbufsize := succ(size or $3FF);
  252.   GetMem(xbuf, xbufsize);
  253.   Result := pByte(xbuf);
  254.   if Result = nil then
  255.     ErrorHalt('no memory');  // probably need error message
  256. end;
  257.  
  258. type
  259.   TCharSet = set of AnsiChar;
  260. function CharInSet(c: Char; theSet: TCharSet): boolean;
  261. {$IFDEF UNICODE}
  262. var
  263.   ac: AnsiChar;
  264. {$ENDIF}
  265. begin
  266. {$IFDEF UNICODE}
  267.   Result := False;
  268.   if c < HIGH(AnsiChar) then
  269.   begin
  270.     ac := AnsiChar(Ord(c) and $FF);
  271.     Result := ac in theSet;
  272.   end;
  273. {$ELSE}
  274.     Result := c in theSet;
  275. {$ENDIF}
  276. end;
  277.  
  278. procedure BadArchive;
  279. begin
  280.   ErrorHaltFmt(SFX_Err_Archive, VStr_ExeName);
  281. end;
  282.  
  283. // find extra data tag
  284. //   IN x => extra data, size = length extra data
  285. //   found x => tag data, size = data size, result = true
  286. //   not found size = <= 0, result = true;
  287. function ExtraData(var x: pByte; var size: integer; tag: word): boolean;
  288. type
  289.   TagHead = packed record
  290.     tg: WORD;
  291.     sz: WORD;
  292.   end;
  293.   pTagHead = ^TagHead;
  294. var
  295.   hed: TagHead;
  296. begin
  297.   Result := False;
  298. //  size := 0;
  299.   while size > (sizeof(TagHead) + 2) do
  300.   begin
  301.     hed := pTagHead(x)^;
  302.     dec(size, sizeof(TagHead));
  303.     if hed.tg = Tag then
  304.     begin                                  
  305.       Result := hed.sz <= size;
  306.       if Result then
  307.       begin
  308.         inc(x, sizeof(TagHead));
  309.         size := hed.sz;
  310.       end
  311.       else
  312.         size := -1; // invalid
  313.       exit;
  314.     end;
  315.     dec(size, hed.sz);
  316.     inc(x, hed.sz + sizeof(TagHead));
  317.   end;
  318.   size := 0;
  319. end;
  320.  
  321. (*----------------------------------------------------------------------------
  322. 3.4.0.0 17 Oct 2007 RA new function check if EOC is needed
  323. *)
  324. function NeedEOC64(const EOC: TZipEndOfCentral): bool;
  325. begin
  326.   Result := ((EOC.TotalEntries = MAX_WORD) or (EOC.CentralOffSet = MAX_UNSIGNED) or
  327.     (EOC.CentralEntries = MAX_WORD) or (EOC.CentralSize = MAX_UNSIGNED) or
  328.     (EOC.ThisDiskNo = MAX_WORD) or (EOC.CentralDiskNo = MAX_WORD));
  329. end;
  330.  
  331. (*----------------------------------------------------------------------------
  332. 3.4.0.0 17 Oct 2007 RA new function locate and read EOC64
  333. result:= 0 = OK ; <0 error
  334. *)
  335. procedure GetEOC64(EOCOffset: word; var EOC64: TZipEOC64);
  336. var
  337.   Posn: int64;
  338.   Loc:  TZip64EOCLocator;
  339. begin
  340.   Posn := EOCOffset - SizeOf(TZip64EOCLocator);
  341.   //  TZip64EOCLocator Loc;
  342.   if (Posn >= 0) then
  343.   begin
  344.     FSeek(Posn, FILE_BEGIN);
  345.     if (FRead(Loc, SizeOf(TZip64EOCLocator)) <> SizeOf(TZip64EOCLocator)) then
  346.       BadArchive;
  347.     if (Loc.LocSig = ZipEOC64LocatorSig) then
  348.     begin
  349.       // locator found
  350.       if (FSeek(int64(Loc.EOC64RelOfs), 0) < 0) then
  351.         BadArchive;
  352.       if (FRead(EOC64, SizeOf(TZipEOC64)) <> SizeOf(TZipEOC64)) then
  353.         BadArchive;
  354.       if (EOC64.EOC64Sig <> ZipEndCentral64Sig) then
  355.         BadArchive;
  356.     end;
  357.   end;
  358. end;
  359.  
  360. (*----------------------------------------------------------------------------
  361. 3.4.0.0 12 May 2007 RA new function
  362.   copy CFH to Z64CFH and read Z64 data if needed
  363. *)
  364. procedure GetZ64Entry(const CFH: TZipCentralHeader; var Z64CFH: TZ64CentralEntry);
  365. var
  366.   xlen: integer;
  367.   p: pByte;
  368.   wsz: word;
  369. begin
  370.   Move(CFH.HeaderSig, Z64CFH.HeaderSig, 22);  // copy headersig to crc32
  371.   Z64CFH.FileNameLen := CFH.FileNameLen;
  372.   Z64CFH.ExtraLen  := CFH.ExtraLen;
  373.   Z64CFH.FileComLen := CFH.FileComLen;
  374.   Z64CFH.IntFileAtt := CFH.IntFileAtt;
  375.   Z64CFH.ExtFileAtt := CFH.ExtFileAtt;
  376.   Z64CFH.ComprSize := CFH.ComprSize;    // values to be corrected for Z64
  377.   Z64CFH.UnComprSize := CFH.UnComprSize;
  378.   Z64CFH.RelOffLocal := CFH.RelOffLocal;
  379.   Z64CFH.DiskStart := CFH.DiskStart;
  380.   Z64CFH.MTime := 0;
  381.   Z64CFH.ATime := 0;
  382.   Z64CFH.CTime := 0;
  383.   if CFH.ExtraLen = 0 then
  384.     Exit; // no extra data
  385.   // any ntfs stamps?
  386.   xlen := CFH.ExtraLen;
  387.   p := xbuf;
  388. //  if ExtraData(p, xlen, NTFS_STAMP_TAG) and (xlen >= 24) then
  389.   if ExtraData(p, xlen, NTFS_STAMP_TAG) and (xlen >= 32) then
  390.   begin
  391.     Inc(p, 4);  // skip Reserved and find sub-tag 1
  392.     if ExtraData(p, xlen, 1) and (xlen >= 24) then
  393.     begin
  394.       Z64CFH.MTime := PXNTFData(p)^.MTime;
  395.       Z64CFH.ATime := PXNTFData(p)^.ATime;
  396.       Z64CFH.CTime := PXNTFData(p)^.CTime;
  397.     end;
  398.   end;
  399.   if (CFH.VersionNeed < 45) {or (CFH.ExtraLen = 0)} then
  400.     Exit; // nocorrection needed
  401.   if (CFH.UnComprSize <> MAX_UNSIGNED) and (CFH.ComprSize <> MAX_UNSIGNED) and
  402.       (CFH.RelOffLocal <> MAX_UNSIGNED) and (CFH.DiskStart = MAX_WORD) then
  403.     Exit; // not Zip64
  404.   xlen := CFH.ExtraLen;
  405.   p := xbuf;
  406.   if not ExtraData(p, xlen, Zip64_data_tag) then
  407.     BadArchive;   // no Zip64 data
  408.   wsz := xlen;
  409.   if (CFH.UnComprSize = MAX_UNSIGNED) then
  410.   begin
  411.     if (wsz < 8) then
  412.       BadArchive;
  413.     Z64CFH.UnComprSize := pInt64(p)^;
  414.     Inc(p, Sizeof(int64));
  415.     wsz := wsz - word(SizeOf(int64));
  416.   end;
  417.   if (CFH.ComprSize = MAX_UNSIGNED) then
  418.   begin
  419.     if (wsz < 8) then  
  420.       BadArchive;
  421.     Z64CFH.ComprSize := pInt64(p)^;
  422.     Inc(p, Sizeof(int64));
  423.     wsz := wsz - word(SizeOf(int64));
  424.   end;
  425.   if (CFH.RelOffLocal = MAX_UNSIGNED) then
  426.   begin
  427.     if (wsz < 8) then
  428.       BadArchive;
  429.     Z64CFH.RelOffLocal := pInt64(p)^;
  430.     Inc(p, Sizeof(int64));
  431.     wsz := wsz - word(SizeOf(int64));
  432.   end;
  433.   if (CFH.DiskStart = MAX_WORD) then
  434.   begin
  435.     if (wsz < 4) then
  436.       BadArchive;
  437.     Z64CFH.DiskStart := pInt64(p)^;
  438.   end;
  439. end;
  440.  
  441. // return default Ansi codepage for locale
  442. function DefCP(LangID: integer): integer;
  443. var
  444.   tmp: array[0..15] of char;
  445.   i: integer;
  446.   c: char;
  447. begin
  448.   Result := 0;
  449.   if GetLocaleInfo(LangID, LOCALE_IDEFAULTANSICODEPAGE, PChar(@tmp[0]), 6) <> 0 then
  450.   begin
  451.     Result := 0;
  452.     i := 0;
  453.     c := tmp[0];
  454.     while CharInSet(c, ['0'..'9']) do
  455.     begin
  456.       Result := (Result * 10) + (ord(c)-ord('0'));
  457.       inc(i);
  458.       if i > 6 then
  459.         break;
  460.       c := tmp[i];
  461.     end;
  462.   end;
  463. end;
  464.  
  465. // set the filetime of an extracted file to the value stored in the archive
  466. procedure FileSetDate(const hFile: THandle; const iAge: integer);
  467. var
  468.   LocalFileTime, FileTime: TFileTime;
  469. begin
  470.   DosDateTimeToFileTime(HIWORD(iAge), LOWORD(iAge), LocalFileTime);
  471.   LocalFileTimeToFileTime(LocalFileTime, FileTime);
  472.   SetFileTime(hFile, nil, nil, @FileTime);
  473. end;
  474.  
  475. (*--------------------------------------------------------------------------*)
  476.  
  477. function UpdC32(Octet: byte; Crc: cardinal): cardinal;
  478. begin
  479.   Result := VArr_CRC32Table[byte(Crc xor cardinal(Octet))] xor
  480.     ((Crc shr 8) and $00FFFFFF);
  481. end;
  482.  
  483. (*--------------------------------------------------------------------------*)
  484.  
  485. // fill crc32 buffer
  486.  
  487. procedure Crc32_Buf(str: PByte; len: integer; var crc: cardinal);
  488. begin
  489.   while len > 0 do
  490.   begin
  491.     crc := UpdC32(byte(str^), crc);
  492.     Inc(str);
  493.     Dec(len);
  494.   end;
  495. end;
  496.  
  497. (*--------------------------------------------------------------------------*)
  498.  
  499. // return the smaller value
  500.  
  501. function Min(const I1, I2: longint): longint;
  502. begin
  503.   if I2 < I1 then
  504.     Result := I2
  505.   else
  506.     Result := I1;
  507. end;
  508.  
  509. (*--------------------------------------------------------------------------*)
  510.  
  511. // unstore the current archive file / uncompressed
  512.  
  513. procedure Unstore;
  514. var
  515.   c: cardinal;
  516.   cNumBytes: TWriteFileWritten;
  517.   OutBuf: PAnsiChar;
  518. begin
  519.   GetMem(OutBuf, Min(VInt_BytesToGo, WSIZE) + 2);
  520.   try
  521.     while VInt_BytesToGo > 0 do
  522.     begin
  523.       cNumBytes := Min(VInt_BytesToGo, WSIZE);
  524.       CheckFRead(OutBuf^, cNumBytes);
  525.       Dec(VInt_BytesToGo, cNumBytes);
  526.       if (VRec_ZipHeader.Flag and 1) = 1 then
  527.         for c := 0 to cNumBytes - 1 do
  528.         begin
  529.           OutBuf[c] := AnsiChar(Byte(OutBuf[c]) xor decrypt_byte);
  530.           {update_keys} UpdateKeys(byte(OutBuf[c]));
  531.         end;
  532.       CheckFWrite(VH_OutFile, OutBuf^, cNumBytes, VStr_OutFile);
  533.       Crc32_Buf(PByte(outbuf), cNumBytes, VDW_CRC32Val);
  534.     end;
  535.   finally
  536.     FreeMem(OutBuf);
  537.   end;
  538. end;
  539.  
  540. (*--------------------------------------------------------------------------*)
  541.  
  542.  
  543. // change input file position
  544.  
  545. function FSeek(const Offset: int64; const MoveMethod: word): int64; //##FR
  546. {$IFDEF VERD6up}
  547. begin
  548.   Result := FileSeek(VH_InFile, Offset, MoveMethod);
  549. end;
  550. {$ELSE}
  551. type
  552.   I64Rec = packed record
  553.     case integer of
  554.       0: (I: int64);
  555.       1: (Lo, Hi: cardinal);
  556.   end;
  557. var
  558.   r: I64Rec;
  559. begin
  560.   r.I  := Offset;
  561.   r.Lo := SetFilePointer(VH_InFile, integer(r.Lo), @r.Hi, MoveMethod);
  562.   if (r.Lo = cardinal(-1)) and (GetLastError <> 0) then
  563.     r.I := -1;
  564.   Result := r.i;
  565. end;
  566.  
  567. {$ENDIF}
  568. (*--------------------------------------------------------------------------*)
  569.  
  570. // read from input file
  571.  
  572. function FRead(var Buffer; const cNum: cardinal): cardinal;
  573. var
  574.   dummy: TWriteFileWritten;
  575. begin
  576.   if ReadFile(VH_InFile, Buffer, cNum, dummy, nil) then
  577.     Result := dummy
  578.   else
  579.     Result := 0;
  580. end;
  581.  
  582. (*--------------------------------------------------------------------------*)
  583.  
  584.  
  585. // read from a file and bail if not all data could be read
  586.  
  587. procedure CheckFRead(var Buffer; const cNumBytes: cardinal);
  588. var
  589.   Read: TWriteFileWritten;
  590. begin
  591.   Read := 0;
  592.  
  593.   if (not ReadFile(VH_InFile, Buffer, cNumBytes, Read, nil)) or
  594.     (cardinal(Read) <> cNumBytes) then
  595.     ErrorHaltID(SFX_Err_ArchiveCorrupted);
  596. end;
  597.  
  598. (*--------------------------------------------------------------------------*)
  599.  
  600.  
  601. // write to a file and bail ifnot all data could be written
  602.  
  603. procedure CheckFWrite(const FH: THandle; const Buffer; const cNumBytes: cardinal;
  604.   const FileName: string);
  605. var
  606.   Written: TWriteFileWritten;
  607. begin
  608.   Written := 0;
  609.   // stop overrun
  610.   if cNumBytes > VInt_MaxWrite then
  611.     ErrorHaltFmt(SFX_Err_CannotWriteFile, FileName);
  612.   // write to memory if file not open and address set
  613.   if (FH = INVALID_HANDLE_VALUE) and (VP_SBuf <> nil) then
  614.   begin
  615.     Move(Buffer, VP_SBuf^, cNumBytes);
  616.     Inc(VP_SBuf, cNumBytes);
  617.     Written := cNumBytes;
  618.   end
  619.   else
  620.  
  621.     // don't know why, but sometimes WriteFile returns FALSE though
  622.     // all bytes have successfully been written, so do not check the API's result
  623.     WriteFile(FH, Buffer, cNumBytes, Written, nil);
  624.  
  625.   // seems to reliably show that all's ok or not
  626.   if cardinal(Written) <> cNumBytes then
  627.     ErrorHaltFmt(SFX_Err_CannotWriteFile, FileName);
  628.   VInt_MaxWrite := VInt_MaxWrite - cNumBytes;
  629. end;
  630.  
  631. (*--------------------------------------------------------------------------*)
  632.  
  633.  
  634. // extract the file's path
  635. function ExtractFilePath(const sFilename: string): string;
  636. var
  637.   i: integer;
  638. begin
  639.   (* Handle archive relative paths *)
  640.   i := Length(sFilename);
  641.   if (i = 3) and (Pos(':', sFilename) > 0) then
  642.     Result := sFilename
  643.   else
  644.   begin
  645.     while (i > 0) and not CharInSet(sFilename[i], ['\', '/', ':']) do
  646.       Dec(i);
  647.     if i > 0 then
  648.     begin
  649.       if CharInSet(sFilename[i], ['\', '/']) then
  650.         if i <> 3 then
  651.           Dec(i)
  652.         else
  653.         if sFilename[2] <> ':' then
  654.           Dec(i);
  655.     end;
  656.     Result := Copy(sFilename, 1, i);
  657.   end;
  658. end;
  659.  
  660. (*--------------------------------------------------------------------------*)
  661.  
  662. // handle relative paths, strip directory name
  663.  
  664. function ExtractFileName(const sFileName: string): string;
  665. var
  666.   I: integer;
  667. begin
  668.   (* Handle archive relative paths *)
  669.   I := Length(sFileName);
  670.   while (I > 0) and not CharInSet(sFileName[I], ['\', '/', ':']) do
  671.     Dec(I);
  672.   Result := Copy(sFileName, I + 1, MaxInt);
  673. end;
  674.  
  675. (*--------------------------------------------------------------------------*)
  676.  
  677.  
  678. // does the directory exist?
  679. function DirectoryExists(const sDir: string): boolean;
  680. var
  681.   Code: integer;
  682. begin
  683.   Code := GetFileAttributes(PChar(sDir));
  684.   Result := (Code <> -1) and ((FILE_ATTRIBUTE_DIRECTORY and Code) <> 0);
  685. end;
  686.  
  687. (*--------------------------------------------------------------------------*)
  688.  
  689. // does the file exist?
  690. function FileExists(const sFileName: string): boolean;
  691. var
  692.   Code: Cardinal;
  693. begin
  694.   Code := GetFileAttributes(PChar(sFileName));
  695.   Result := (Code <> Cardinal(-1));
  696. end;
  697.  
  698. (*--------------------------------------------------------------------------*)
  699.  
  700. // force the existence of a directory (and its parents)
  701.  
  702. function ForceDirectories(sDir: string): boolean;
  703. begin
  704.   Result := True;
  705.   sDir := RemoveDirSeparator(sDir);
  706.   if Length(sDir) = 0 then
  707.     exit;
  708.   if DirectoryExists(sDir) or (ExtractFilePath(sDir) = sDir) then
  709.     Exit; // avoid 'xyz:\' problem.
  710.   if not ForceDirectories(ExtractFilePath(sDir)) then
  711.     Result := False
  712.   else
  713.     Result := CreateDirectory(PChar(sDir), nil);
  714. end;
  715.  
  716. (*--------------------------------------------------------------------------*)
  717.  
  718. // ensure trailing backslash
  719.  
  720. function AppendDirSeparator(const sDir: string): string; //##FR modified
  721. var
  722.   i: integer;
  723. begin
  724.   i := Length(sDir);
  725.   Result := sDir;
  726.   if i > 0 then
  727.     if Result[i] <> Chr_DirSep then
  728.       Result := Result + Chr_DirSep;
  729. end;
  730.  
  731. (*--------------------------------------------------------------------------*)
  732.  
  733. // ensure NO trailing backslash
  734.  
  735. function RemoveDirSeparator(const sDir: string): string;
  736. begin
  737.   Result := sDir;
  738.   while (Length(Result) > 0) and (Result[Length(Result)] = Chr_DirSep) do
  739.     SetLength(Result, Length(Result) - 1);
  740. end;
  741.  
  742. (*--------------------------------------------------------------------------*)
  743.  
  744. // expand environment variables
  745.  
  746. function ExpandEnv(const Str: string): string;
  747. var
  748.   pch: PChar;
  749. begin
  750.   GetMem(pch, MAX_PATH * 2);
  751.   try
  752.     FillChar(pch^, MAX_PATH * 2, 0);
  753.     if ExpandEnvironmentStrings(PChar(Str), pch, (MAX_PATH * 2) - 1) > 0 then
  754.       Result := pch
  755.     else
  756.       Result := '';
  757.   finally
  758.     FreeMem(pch);
  759.   end;
  760. end;
  761.  
  762. (*--------------------------------------------------------------------------*)
  763.  
  764. // show a message box
  765.  
  766. function MsgBox(const wndpar: HWND; const sMsg, sTitle: string;
  767.   const uType: cardinal): integer;
  768. begin
  769.   Result := MessageBox(wndPar, PChar(sMsg), PChar(sTitle), uType or MB_TASKMODAL);
  770. end;
  771.  
  772. (*--------------------------------------------------------------------------*)
  773. // show an error message
  774.  
  775. procedure ErrorMsgBox(const wndPar: HWND; const sMsg: string);
  776. begin
  777.   MsgBox(wndPar, sMsg, PChar(SFXString(SFX_Cap_Err)), MB_ICONSTOP);
  778. end;
  779.  
  780. procedure ErrorMsgBoxFmt1(const wndPar: HWND; id: integer; const arg1: string);
  781. begin
  782.   ErrorMsgBox(wndPar, FmtStrID1(id, arg1));
  783. end;
  784.  
  785. (*--------------------------------------------------------------------------*)
  786.  
  787. // compare two strings / case insensitive
  788.  
  789. function CompareText(const s1, s2: string): boolean;
  790. begin
  791.   Result := (Length(s1) = Length(s2)) and (lstrcmpi(PChar(s1), PChar(s2)) = 0);
  792. end;
  793.  
  794. (*--------------------------------------------------------------------------*)
  795.  
  796. // to check correct file size of the input file +++ 08/13/98
  797.  
  798. function FindEOCRecord: cardinal;
  799. var
  800.   pRec: PZipEndOfCentral;
  801.   cBufferSize, cRead, cFilePos: cardinal;
  802.   pBuffer: PAnsiChar;
  803.   cCurrentPos: cardinal;
  804.   c: cardinal;
  805.   //loop counter only var for compiler optimization (register value) ##FR
  806.   bOK: boolean;
  807. begin
  808.   Result := HIGH(cardinal);
  809.   // get the needed size of the buffer ( max 65536 + SizeOf( eocd ), min SizeOf( file ) )
  810.   bOK := False;
  811.   cBufferSize := GetFileSize(VH_InFile, nil) - cardinal(VInt_FileBegin);
  812.   if cBufferSize > 65558 then
  813.     cBufferSize := 65558;
  814.  
  815.   if cBufferSize > sizeof(TZipEndOfCentral) then
  816.     //if smaller, then no correct zip file
  817.   begin
  818.     GetMem(pBuffer, cBufferSize);
  819.     try
  820.       cCurrentPos := FSeek(0, FILE_CURRENT); //##FR mark the current file pos
  821.       cFilePos := FSeek(-cBufferSize, FILE_END);
  822.       //FSeek is now a function, not a proc, see sfxmisc.pas
  823.       cRead := FRead(pBuffer[0], cBufferSize);
  824.       FSeek(cCurrentPos, FILE_BEGIN); //##FR jump back to marked filepos
  825.       if cRead = cBufferSize then
  826.         for c := 0 to cBufferSize - sizeof(TZipEndOfCentral) do
  827.         begin
  828.           pRec := Pointer(cardinal(pBuffer) + c);
  829.           if pRec^.HeaderSig = ZipEndOfCentralSig then
  830.           begin
  831.             // eocd is found, now check if size is correct ( = pos+22+eocd.commentsize)
  832.             if (pRec^.ZipCommentLen + cFilePos + c +
  833.               sizeof(TZipEndOfCentral)) = GetFileSize(VH_InFile, nil) then
  834.             begin
  835.               bOK := True; // set ok flag
  836.               Result := cFilePos + c;
  837.               Break;
  838.             end;
  839.           end;
  840.         end;
  841.     finally
  842.       FreeMem(pBuffer);
  843.     end;
  844.   end;
  845.  
  846.   if not bOK then
  847.     ErrorHaltID(SFX_Err_ArchiveCorrupted);
  848. end;
  849.  
  850. (*--------------------------------------------------------------------------*)
  851.  
  852. // get the index of a string in an array / case insensitive
  853.  
  854. function StrArrayIndexOf(s1: string; const args: array of string): integer;
  855. var
  856.   i: integer;
  857. begin
  858.   Result := -1;
  859.   for i := Low(args) to High(args) do
  860.     if CompareText(s1, args[i]) then
  861.     begin
  862.       Result := i;
  863.       Break;
  864.     end;
  865. end;
  866.  
  867. (*--------------------------------------------------------------------------*)
  868.  
  869. // read a sfx header string
  870. {$IFDEF UNICODE}
  871. procedure ReadSFXString(var sToRead: string; const iLen: integer);
  872. var
  873.   utf8s: UTF8String;
  874. begin
  875.   if iLen > 0 then
  876.   begin
  877.     SetLength(utf8s, iLen);
  878.     CheckFRead(utf8s[1], iLen);
  879.   end;
  880.   sToRead := String(utf8s);
  881. end;
  882. {$ELSE}
  883. procedure ReadSFXString(var sToRead: string; const iLen: integer);
  884. begin
  885.   if iLen > 0 then
  886.   begin
  887.     SetLength(sToRead, iLen);
  888.     CheckFRead(sToRead[1], iLen);
  889.   end;
  890. end;
  891. {$ENDIF}
  892.  
  893. (*--------------------------------------------------------------------------*)
  894.  
  895. // read a path from registry or return ''
  896. function GetPathFromRegistry(sPath: string): string;
  897. var
  898.   sRoot, sValue, sSuffix, sData: string;
  899.   i: integer;
  900.   c: cardinal;
  901.   hkRoot, hkOpen: HKEY;
  902. begin
  903.   // format hkxy\reg path\value[|suffix]
  904.   Result := '';
  905.   sRoot  := '';
  906.   sValue := '';
  907.   sSuffix := '';
  908.  
  909.   i := Pos(Chr_DirSep, sPath); // root\...
  910.   if i > 0 then
  911.   begin
  912.     sRoot := Copy(sPath, 1, i - 1);
  913.     Delete(sPath, 1, i);
  914.     i := Pos('|', sPath);  // ...|suffix
  915.     if i > 0 then
  916.     begin
  917.       sSuffix := Copy(sPath, i + 1, MaxInt);
  918.       Delete(sPath, i, MaxInt);
  919.     end;
  920.  
  921.     i := Length(sPath);
  922.     while (i > 0) and (sPath[i] <> Chr_DirSep) do
  923.       Dec(i);
  924.     if (i > 0) and (i < Length(sPath)) then // ..\value
  925.     begin
  926.       sValue := Copy(sPath, i + 1, MaxInt);
  927.       Delete(sPath, i, MaxInt);
  928.     end;
  929.  
  930.     case StrArrayIndexOf(sRoot, ['HKEY_CURRENT_USER', 'HKCU',
  931.         'HKEY_LOCAL_MACHINE', 'HKLM', 'HKEY_USERS', 'HKU']) of
  932.       0, 1: hkRoot := HKEY_CURRENT_USER;
  933.       2, 3: hkRoot := HKEY_LOCAL_MACHINE;
  934.       4, 5: hkRoot := HKEY_USERS;
  935.       else
  936.         hkRoot := 0;
  937.     end;
  938.     if (hkRoot <> 0) and (RegOpenKey(hkRoot, PChar(sPath), hkOpen) =
  939.       ERROR_SUCCESS) then
  940.     begin
  941.       SetLength(sData, MAX_PATH * 2);
  942.       c := Length(sData);
  943.       if RegQueryValueEx(hkOpen, PChar(sValue), nil, nil, PByte(PChar(sData)),
  944.         @c) = ERROR_SUCCESS then
  945.       begin
  946.         SetLength(sData, c - 1); // assumed to be reg_sz or reg_expand_sz
  947.         i := Pos(';', sData);
  948.         if i > 0 then
  949.           Delete(sData, i, MaxInt);
  950.         Result := AppendDirSeparator(PChar(sData)) + sSuffix;
  951.       end;
  952.       RegCloseKey(hkRoot);
  953.     end;
  954.   end;
  955. end;
  956.  
  957. (*--------------------------------------------------------------------------*)
  958.  
  959. // expand path to include drive
  960. function ExpandPath(const sRel: string): string;
  961. var
  962.   p: PChar;
  963. begin
  964.   SetLength(Result, MAX_PATH * 2);
  965.   SetLength(Result, GetFullPathName(PChar(sRel), Length(Result), PChar(Result), p));
  966. end;
  967.  
  968. (*--------------------------------------------------------------------------*)
  969.  
  970. // build a volatile path name (sfx_<unique name>)
  971. function GetUniqueVolatilePath: string;
  972. var
  973.   LStrDir:  string;
  974.   LIntLoop: integer;
  975. begin
  976.   VBool_CheckDeleteVolatilePath := False;
  977.   Result := AppendDirSeparator(ExpandEnv('%temp%')) + 'sfx';
  978.   for LIntLoop := 0 to 99999 do // just 8 chars (dos conventions)
  979.   begin
  980.     LStrDir := Int2Str(LIntLoop, 0);
  981.     LStrDir := Copy('0000', 1, 5 - Length(LStrDir)) + LStrDir;
  982.     if not FileExists(Result + LStrDir) then
  983.     begin
  984.       Result := AppendDirSeparator(Result + LStrDir);
  985.       VBool_CheckDeleteVolatilePath := True;
  986.       VStr_VolatilePath := Result; // remember volatile path
  987.       VStr_VolatilePath_Unexpanded := 'sfx' + LStrDir;
  988.       Break;
  989.     end;
  990.   end;
  991. end;
  992.  
  993. (*--------------------------------------------------------------------------*)
  994. function PRIMARYLANGID(lang: LANGID): LANGID; inline;
  995. begin
  996.   Result := WORD(lang) and $3FF;
  997. end;
  998.  
  999. procedure ClearLang(var dest: PByte);
  1000. begin
  1001.     ReAllocMem(dest, 0);
  1002.     VInt_CP := 0;
  1003. end;
  1004.  
  1005. // MaxCSize protects agains overruns on invalid data
  1006. function LoadStrings(var dest: PByte; MaxCSize: Integer): integer;
  1007. var
  1008.   DHead: TSFXStringsData;
  1009. begin
  1010.   // load strings
  1011.   Result := 0;
  1012.   ReAllocMem(dest, 0);
  1013.   CheckFRead(DHead, sizeof(TSFXStringsData));
  1014.   if DHead.CSize > MaxCSize then
  1015.     Result := -1  // failed sanity check
  1016.   else
  1017.   begin
  1018.     ReAllocMem(dest, DHead.USize + 4);
  1019.     VP_SBuf := dest;
  1020.     VDW_CRC32Val := CRC_MASK;
  1021.     VInt_MaxWrite := DHead.USize;
  1022.     VInt_BytesToGo := DHead.CSize;
  1023.     InFlate(nil, 0);
  1024.   end;
  1025.   if (Result <> 0) or (DHead.CRC <> (VDW_CRC32Val xor $FFFFFFFF)) then
  1026.   begin
  1027.     ClearLang(dest);
  1028.     Result := -1;
  1029.   end;
  1030. end;
  1031.  
  1032.  
  1033. // Set language strings
  1034. procedure SetLangStrings(hLC: hWnd);
  1035. var
  1036.   UILang: LANGID;
  1037.   i, lng: integer;
  1038.   idx: cardinal;
  1039.   PHead: PSFX_LanguageData;
  1040.   tmp: PChar;
  1041. begin
  1042.   if (hLC = 0) {or (VRec_SHeader.Count < 1) or (pl = nil)} then
  1043.     exit;
  1044.   UILang := GetUserDefaultLangID;
  1045.   idx := 0;
  1046.  
  1047.   tmp := PChar(GetXBuf(256));
  1048.   // add US (English) first
  1049. //  if GetLocaleInfo($0409, LOCALE_SNATIVELANGNAME, tmp, 120) <> 0 then
  1050. //    SendMessage(hLC, CB_ADDSTRING, 0, integer(tmp));
  1051.   SendMessage(hLC, CB_ADDSTRING, 0, LPARAM(pChar('Default (US)')));
  1052.   i := 1;
  1053.   PHead := LoadResource(SFX_LANG_BASE + i);
  1054.   while (PHead <> nil) and (PHead^.LangID <> 0) do
  1055.   begin
  1056.     lng := PHead^.LangID;
  1057.     if GetLocaleInfo(Lng, LOCALE_SNATIVELANGNAME, tmp, 120) <> 0 then
  1058.     begin
  1059.       SendMessage(hLC, CB_ADDSTRING, 0, LPARAM(tmp));
  1060.       if PRIMARYLANGID(lng) = PRIMARYLANGID(UILang) then
  1061.         idx := i;
  1062.     end;
  1063.     inc(i);
  1064.     PHead := LoadResource(SFX_LANG_BASE + i);
  1065.   end;
  1066.   SendMessage(hLC, CB_SETCURSEL, idx, 0); // set default
  1067. end;
  1068.  
  1069.  
  1070. function LoadLang(var dest: PByte; resID: integer): integer;
  1071. const
  1072.   MaxCSize = 10000; // ???
  1073. var
  1074.   PHead: PSFX_LanguageData;
  1075.   p: PAnsiChar;
  1076. begin
  1077.   // load selected strings
  1078.   Result := 0;
  1079.   ClearLang(dest);
  1080.   if resID <= 0 then
  1081.     exit;   // use default
  1082.   Result := -1;
  1083.   PHead := LoadResource(resID);
  1084.   if (PHead <> nil) and (PHead^.CSize < MaxCSize) then
  1085.   begin
  1086.     Result := PHead^.LangID;
  1087.     VInt_CP := DefCP(Result);
  1088.     ReAllocMem(dest, PHead.USize + 4);
  1089.     VP_SBuf := dest;
  1090.     VDW_CRC32Val := CRC_MASK;
  1091.     VInt_MaxWrite := PHead.USize;
  1092.     VInt_BytesToGo := PHead.CSize;
  1093.     p := PAnsiChar(PHead);
  1094.     inc(p, sizeof(TSFX_LanguageData)); // point to data
  1095.     InFlate(p ,PHead.CSize);
  1096.   end;
  1097.   if (Result <= 0) or (PHead.CRC <> (VDW_CRC32Val xor $FFFFFFFF)) then
  1098.   begin
  1099.     ClearLang(dest);
  1100.     Result := -1;
  1101.   end;
  1102.   VP_SBuf := nil;
  1103. end;
  1104.  
  1105. procedure SetLanguage;
  1106. var
  1107.   Def: LANGID;
  1108.   i, pri: integer;
  1109.   psd: PSFX_LanguageData;
  1110. begin
  1111.   VRec_Strings := nil;
  1112.   // strings are optional
  1113.   psd := LoadResource(SFX_LANG_BASE + 1);
  1114.   if psd <> nil then
  1115.   begin
  1116.     // we have strings - load initial
  1117.     pri := -1;
  1118.     // try for 'default' language
  1119.     Def := GetUserDefaultLangID;
  1120.     if Def <> $0409 then
  1121.     begin
  1122.       i := 1;//0;
  1123.       while psd <> nil do
  1124.       begin
  1125.         if psd^.LangID = Def then
  1126.           break;
  1127.         if (PRIMARYLANGID(psd^.LangID) = PRIMARYLANGID(Def)) then
  1128.           pri := i;
  1129.         inc(i);
  1130.         psd := LoadResource(SFX_LANG_BASE + i); // try next
  1131.       end;
  1132.       if pri > 0 then
  1133.         LoadLang(VRec_Strings, SFX_LANG_BASE + pri);
  1134.     end;
  1135.     // Display the dialog
  1136.     if DialogBox(hInstance, Str_Dlg_Language, 0, @LanguageQueryDialogProc) =
  1137.         ID_BTN_NO then
  1138.       Halt;
  1139.  
  1140.     // load selected strings
  1141.     if (VInt_CurLang <> pri) then
  1142.     begin
  1143.       if (VInt_CurLang > 0) then
  1144.         LoadLang(VRec_Strings, SFX_LANG_BASE + VInt_CurLang)
  1145.       else
  1146.         ClearLang(VRec_Strings);
  1147.     end;
  1148.   end;
  1149.   VP_SBuf := nil;
  1150. end;
  1151.  
  1152. //  Returns a boolean indicating whether or not we're running under XP or later.
  1153. function WinVersion: integer;
  1154. var
  1155.   osv: TOSVERSIONINFO;
  1156. begin
  1157.   osv.dwOSVersionInfoSize := sizeOf(OSVERSIONINFO);
  1158.   GetVersionEx(osv);
  1159.   Result := (osv.dwMajorVersion *1000) + osv.dwMinorVersion;
  1160. end;
  1161.  
  1162. // check manual override of AutoRun
  1163. function Manual: Boolean;
  1164. var
  1165.   cp: PChar;
  1166.   c0, c1, c2: Char;
  1167. begin
  1168.   Result := False;
  1169.   cp := GetCommandLine;
  1170.   if cp = nil then
  1171.     Exit;
  1172.   c0 := #0;
  1173.   c1 := #0;
  1174.   c2 := #0;
  1175.   while cp^ <> #0 do
  1176.   begin
  1177.     c0 := c1;
  1178.     c1 := c2;
  1179.     c2 := cp^;
  1180.     cp := CharNext(cp);
  1181.   end;
  1182.   if c0 > ' ' then
  1183.     Exit;
  1184.   if c1 <> '/' then
  1185.     Exit;
  1186.   Result := (c2 = 'm') or (c2 = 'M');
  1187. end;
  1188.  
  1189. // read TSFXFileHeader from input file
  1190. procedure GetDefParams; // reads the values from the special header
  1191. type
  1192.   T_DetachedArgs = packed record
  1193.     Size: WORD;    // size of full record including sig
  1194.     Pads: WORD;    // number of bytes to keep DWORD aligned
  1195.   end;
  1196. var
  1197.   Sig: cardinal;
  1198.   CmdStrs: PByte;
  1199.   PathSize: Integer;
  1200.   StartMsgSize: Integer;
  1201. begin
  1202.   CmdStrs := nil;
  1203.   VInt_FileBegin := GetExeSize;
  1204.   VP_SBuf := nil;
  1205.   FSeek(VInt_FileBegin, FILE_BEGIN);
  1206.  
  1207.   CheckFRead(VRec_SFXHeader, sizeof(VRec_SFXHeader));
  1208.  
  1209.   with VRec_SFXHeader do
  1210.   begin
  1211.     if (Signature <> SFX_HEADER_SIG) then
  1212.       ErrorHaltID(SFX_Err_ArchiveCorrupted);
  1213.     // get command strings
  1214.     if (so_CompressedCmd and Options) <> 0 then
  1215.     begin
  1216.       if LoadStrings(CmdStrs, VRec_SFXHeader.Size - sizeof(VRec_SFXHeader)) <> 0 then
  1217.         ErrorHaltID(SFX_Err_ArchiveCorrupted);
  1218.       VP_SBuf := nil;
  1219.       FSeek(VInt_FileBegin + VRec_SFXHeader.Size, FILE_BEGIN);
  1220.     end
  1221.     else
  1222.     begin
  1223.       ReAllocMem(CmdStrs, size - sizeof(TSFXFileHeader));
  1224.       CheckFRead(CmdStrs^, size - sizeof(TSFXFileHeader));
  1225.     end;
  1226.     // check for sfx header
  1227.     VInt_SpanType := SFXSpanTypeNone;  // default
  1228.  
  1229.     VStr_SFX_Caption := LoadSFXStr(CmdStrs, sc_Caption);
  1230.     if VStr_SFX_Caption = '' then
  1231.       VStr_SFX_Caption := SFXString(SFX_Cap_App);
  1232.  
  1233.     VStr_SFX_Path := LoadSFXStr(CmdStrs, sc_Path);
  1234.     VStr_SFX_CmdLine := LoadSFXStr(CmdStrs, sc_CmdLine);
  1235.     VStr_SFX_RegFailPath := LoadSFXStr(CmdStrs, sc_RegFailPath);
  1236.     VStr_SFX_StartMsg := LoadSFXStr(CmdStrs, sc_StartMsg);
  1237.     ReAllocMem(CmdStrs, 0);  // finished with it
  1238.     PathSize := Length(VStr_SFX_Path);
  1239.     StartMsgSize := Length(VStr_SFX_StartMsg);
  1240.  
  1241.     //get the path from registry, added 10/10/98 ##FR
  1242.     if CompareText('HK', Copy(VStr_SFX_Path, 1, 2)) then
  1243.     begin
  1244.       VStr_SFX_Path := GetPathFromRegistry(VStr_SFX_Path);
  1245.       if VStr_SFX_Path = '' then
  1246.       begin
  1247.         if VStr_SFX_RegFailPath <> '' then
  1248.           VStr_SFX_Path := VStr_SFX_RegFailPath
  1249.         else
  1250.           VStr_SFX_Path := '><'; // substitue to temp path below
  1251.       end;
  1252.     end;
  1253.  
  1254.     while Pos('><', VStr_SFX_Path) > 0 do
  1255.       VStr_SFX_Path := FmtStr1(VStr_SFX_Path, AppendDirSeparator(ExpandEnv('%temp%')));
  1256.  
  1257.     // added april 20, 2002: substitute environment variables
  1258.     if (so_ExpandVariables and Options) <> 0 then
  1259.     begin
  1260.       VStr_SFX_Path := ExpandEnv(VStr_SFX_Path);
  1261.       VStr_SFX_Caption := ExpandEnv(VStr_SFX_Caption);
  1262.       VStr_SFX_StartMsg := ExpandEnv(VStr_SFX_StartMsg);
  1263.       VStr_SFX_CmdLine := ExpandEnv(VStr_SFX_CmdLine);
  1264.     end;
  1265.  
  1266.     if PathSize = 0 then // Stored path
  1267.       VStr_ExtractPath := GetCurDir
  1268.     else
  1269.     begin
  1270.       // aug 26, 2002: added support for volatile extract directory
  1271.       if CompareText(VStr_SFX_Path, '<VOLATILE>') then
  1272.         VStr_ExtractPath := GetUniqueVolatilePath
  1273.       else
  1274.         VStr_ExtractPath := ExpandPath(VStr_SFX_Path);
  1275.     end;
  1276.  
  1277.     // shall we show the message ?
  1278.     if (StartMsgSize > 0) and (MsgBox(0, VStr_SFX_StartMsg, VStr_SFX_Caption,
  1279.       StartMsgType) in [idCancel, idAbort, idNo, IDCLOSE]) then
  1280.       Halt;
  1281.  
  1282.     // check autorun flag
  1283.     if ((so_AutoRun or so_CheckAutoRunFileName) and Options) =
  1284.       (so_AutoRun or so_CheckAutoRunFileName) then
  1285.     begin
  1286.       if ((WinVersion >= 6000) and (nvVerifyTrust(VStr_ExeName) <> 0)) or Manual or
  1287.         (CompareText(ExtractFileName(VStr_ExeName), 'Setup.exe') or
  1288.         (ExtractFileName(VStr_ExeName)[1] = '!')) then
  1289.         Options := Options and (not so_AutoRun);
  1290.     end;
  1291.   end;
  1292.  
  1293.   // at beginning of file or detached header
  1294.   // might have detached header
  1295.   CheckFRead(Sig, sizeof(Sig));
  1296. (*@@
  1297.   if (Sig = SFX_DETACHED_HEADER_SIG) then
  1298.   begin
  1299.     // load detached name
  1300.     CheckFRead(DetArgs, sizeof(DetArgs));
  1301.     i := DetArgs.Size - SizeOf(TSFXDetachedHeader) - DetArgs.Pads;
  1302.     ReadSFXString(VStr_DetachName, i);
  1303.     // skip pads
  1304.     if DetArgs.Pads > 0 then
  1305.       FSeek(DetArgs.Pads, FILE_CURRENT);
  1306.     CheckFRead(Sig, sizeof(Sig));
  1307.   end;
  1308. *)
  1309.   // check the signature following
  1310.   if Sig = ZipCentralHeaderSig then
  1311.     VInt_SpanType := SFXSpanTypeMultiVol; // is span but type unknown
  1312.   // reposition to before local/central signature
  1313.   VInt_FileBegin := FSeek(-sizeof(Sig), FILE_CURRENT);
  1314. end;
  1315.  
  1316. (*--------------------------------------------------------------------------*)
  1317.  
  1318. //// get operating system type (nt/win)
  1319. //function IsWinNT: boolean;
  1320. //var
  1321. //  osvi: TOSVersionInfo;
  1322. //begin
  1323. //  osvi.dwOSVersionInfoSize := SizeOf(OSvi);
  1324. //  Result := GetVersionEx(OSVI) and (osvi.dwPlatformID = VER_PLATFORM_WIN32_NT);
  1325. //end;
  1326.  
  1327. (*--------------------------------------------------------------------------*)
  1328.  
  1329. //##FR execute inf-scripts using rundll, not nice but works!
  1330.  
  1331. function ExecInf(const Path: String): cardinal;
  1332. (*var
  1333.   osvi: TOSVersionInfo;*)
  1334. //var
  1335. //  cmd: AnsiString;
  1336. //  HINSTANCE: HINST;
  1337. begin
  1338. //  HINSTANCE
  1339.   Result := ShellExecute(VH_MainWnd, PChar('open'), PChar('rundll32.exe'),
  1340.   PChar('SetupApi,InstallHinfSection DefaultInstall 132 ' + Path),
  1341.          nil, SW_SHOW);
  1342. //  Result := HINSTANCE;// > 32;
  1343.   (*if Param = '.ntx86' then    Param := Param + ' '
  1344.   else
  1345.     Param := '';
  1346.  
  1347.   if IsWinNT
  1348.   then
  1349.     Path := 'rundll32.exe setupapi.dll,
  1350.         InstallHinfSection DefaultInstall' + Param + '132 ' + Path
  1351.   else*)
  1352. //  cmd := 'rundll.exe setupapi.dll,InstallHinfSection DefaultInstall 132 '
  1353. //    + AnsiString(Path);
  1354. //  Result := WinExec(PAnsiChar(cmd), SW_SHOW);
  1355. //  Result := WinExec(PAnsiChar(
  1356. //    'rundll.exe setupapi.dll,InstallHinfSection DefaultInstall 132 '
  1357. //    + Path), SW_SHOW);
  1358.   (*osvi.dwOSVersionInfoSize := SizeOf(OSvi);
  1359.   if GetVersionEx(OSVI) then
  1360.   begin
  1361.     case osvi.dwPlatformID of
  1362.       VER_PLATFORM_WIN32_WINDOWS: Path :=
  1363.           'rundll.exe setupx.dll,InstallHinfSection DefaultInstall 132 ' + Path;
  1364.       VER_PLATFORM_WIN32_NT: Path :=
  1365.           'rundll32.exe setupapi.dll,InstallHinfSection DefaultInstall' +
  1366.           Param + '132 ' + Path;
  1367.       else
  1368.         // no win32 s
  1369.     end;
  1370.     Result := WinExec(PChar(Path), SW_SHOW);
  1371.   end;*)
  1372.  
  1373. end;
  1374.  
  1375. (*--------------------------------------------------------------------------*)
  1376.  
  1377. function Trim(s: string): string;
  1378.   // strip trailing #0 and double separators
  1379. begin
  1380.   Result := PChar(s);
  1381.   while Pos('\\', Result) > 0 do
  1382.     Delete(Result, Pos('\\', Result), 1);
  1383. end;
  1384.  
  1385. (*--------------------------------------------------------------------------*)
  1386.  
  1387. // remove extract directory next time a user logs on
  1388.  
  1389. procedure RemoveDirEx;
  1390. var
  1391.   LStrCmd: string;
  1392.   LHKSub:  HKEY;
  1393.   LBoolSuccess: boolean;
  1394. begin
  1395. //  if IsWinNT then
  1396. //  if Win32Platform = VER_PLATFORM_WIN32_NT then
  1397.   if WinVersion >= 5000 then
  1398.     // the following does not work with all types of drives
  1399.     (*LStrCmd := 'cmd.exe /c @if exist "'+RemoveDirSeparator(VStr_VolatilePath)+
  1400.       '\nul" rd /s /q "'+RemoveDirSeparator(VStr_VolatilePath)+'"'*)
  1401.     LStrCmd := 'cmd.exe /c @rd /s /q "%temp%\' + VStr_VolatilePath_Unexpanded + '">nul 2>nul'
  1402.   else
  1403.     // the following does not work with all types of drives
  1404.     (*LStrCmd := 'command.com /c @if exist "'+RemoveDirSeparator(VStr_VolatilePath)+
  1405.       '\nul" deltree /y "'+RemoveDirSeparator(VStr_VolatilePath)+'"';*)
  1406.     LStrCmd := 'command.com /c deltree /y "%temp%\' + VStr_VolatilePath_Unexpanded + '">nul';
  1407.  
  1408.   if RegCreateKey(HKEY_LOCAL_MACHINE,
  1409.     'Software\Microsoft\Windows\CurrentVersion\RunOnce', LHKSub) = ERROR_SUCCESS then
  1410.   begin
  1411.     LBoolSuccess := RegSetValueEx(LHKSub, PChar(VStr_VolatilePath_Unexpanded),
  1412.       0, REG_SZ, PChar(LStrCmd), Length(LStrCmd) + 1) = ERROR_SUCCESS;
  1413.     RegCloseKey(LHKSub);
  1414.   end
  1415.   else
  1416.     LBoolSuccess := False;
  1417.  
  1418.   // try current user if not successfull
  1419.   if (not LBoolSuccess) and (RegCreateKey(HKEY_CURRENT_USER,
  1420.     'Software\Microsoft\Windows\CurrentVersion\RunOnce', LHKSub) =
  1421.     ERROR_SUCCESS) then
  1422.   begin
  1423.     RegSetValueEx(LHKSub, PChar(VStr_VolatilePath_Unexpanded), 0,
  1424.       REG_SZ, PChar(LStrCmd), Length(LStrCmd) + 1);
  1425.     RegCloseKey(LHKSub);
  1426.   end;
  1427. end;
  1428.  
  1429. (*--------------------------------------------------------------------------*)
  1430.  
  1431. //##FR modified to enable inf-scripts
  1432.  
  1433. function ExecuteCMD: cardinal;
  1434.   // parses and executes the stored command line after extraction
  1435. var
  1436.   sr1, sr2: string;
  1437.   srOld: string;
  1438. begin
  1439.   Result := 0;
  1440.     sr1 := Trim(GetArgument(1));
  1441.     sr2 := Trim(GetArgument(2));
  1442.     srOld := GetCurDir;
  1443.     if Length(VStr_ExtractPath) <> 0 then
  1444.       ChDir(VStr_ExtractPath);
  1445.     if Length(sr1) > 4 then
  1446.     begin
  1447.       if TestForInf(sr1) then
  1448.         Result := ExecInf(sr1) //error if < 32
  1449.       else
  1450.         Result := ShellExecute(0, 'open', PChar(sr1), PChar(sr2),
  1451.           PChar(VStr_ExtractPath), SW_SHOW);
  1452.     end;
  1453.     ChDir(srOld);
  1454.   // aug 26, 2002: added support for volatile extract path
  1455.   if (Result >= 32) and VBool_CheckDeleteVolatilePath and
  1456.     CompareText(RemoveDirSeparator(VStr_VolatilePath),
  1457.     RemoveDirSeparator(VStr_ExtractPath)) and
  1458.     DirectoryExists(RemoveDirSeparator(VStr_VolatilePath)) then
  1459.     RemoveDirEx;
  1460. end;
  1461.  
  1462. (*--------------------------------------------------------------------------*)
  1463.  
  1464.  
  1465. function FmtStrID1(id: integer; const arg1: string): string;
  1466. begin
  1467.   Result := FmtStr1(SFXString(id), arg1);
  1468. end;
  1469.  
  1470. function FmtStr1(const sFormat: string; const arg1: string): string;
  1471. var
  1472.   j: integer;
  1473. begin
  1474.   Result := sFormat;
  1475.   j := Pos('><', Result);
  1476.   if j > 0 then
  1477.     Result := Copy(Result, 1, j - 1) + arg1 + Copy(Result, j + 2, MaxInt);
  1478. end;
  1479.  
  1480. function FmtStr2(const sFormat: string; const arg1, arg2: string): string;
  1481. begin
  1482.   Result := FmtStr1(FmtStr1(sFormat, arg1), arg2);
  1483. end;
  1484.  
  1485. (*--------------------------------------------------------------------------*)
  1486.  
  1487. function GetArgument(const iIndex: integer): string;
  1488.   // gets an argument from the stored command line
  1489.   //                1 : the part before the pipe (if there's no pipe,
  1490.   //                                      returns the whole command line)
  1491.   //                2 : the part after the pipe (if no pipe, returns "")
  1492.   //                all "><" will be replaced by the extraction path
  1493. var
  1494.   pip: integer;
  1495. begin
  1496.   VStr_ExtractPath := AppendDirSeparator(VStr_ExtractPath);
  1497.   Result := VStr_SFX_CmdLine;
  1498.   pip := Pos('|', Result);
  1499.   if pip = 0 then
  1500.   begin
  1501.     if iIndex = 2 then
  1502.       Result := '';
  1503.   end
  1504.   else
  1505.   begin
  1506.     if iIndex = 1 then
  1507.       Result := Copy(Result, 1, pip - 1)
  1508.     else
  1509.       Result := Copy(Result, pip + 1, MAXINT);
  1510.   end;
  1511.  
  1512.   while Pos('><', Result) > 0 do
  1513.     Result := FmtStr1(Result, VStr_ExtractPath);
  1514.  
  1515.   // get the short (8+3)-filename (it seems that shellexecute has some problems with lfn)
  1516.   GetShortPathName(PChar(Result), PChar(Result), Length(Result));
  1517. end;
  1518.  
  1519. (*--------------------------------------------------------------------------*)
  1520.  
  1521. function TestForInf(const sr1: string): boolean;
  1522. begin
  1523.   Result := CompareText('.inf', Copy(sr1, Length(sr1) - 3, 4));
  1524. end;
  1525.  
  1526. (*--------------------------------------------------------------------------*)
  1527.  
  1528. function GetRunString(const sRun, sInst: string): string;
  1529. var
  1530.   sr1: string;
  1531. begin
  1532.   sr1 := ExtractFileName(GetArgument(1));
  1533.   if TestForInf(sr1) then
  1534.     Result := FmtStr2(sRun, sr1, ExtractFileName(GetArgument(2)))
  1535.   else
  1536.   begin
  1537.     if sr1 = '' then
  1538.     begin
  1539.       sr1 := GetArgument(1);
  1540.       if sr1 <> '' then
  1541.         sr1 := ExtractFileName(RemoveDirSeparator(sr1));
  1542.     end;
  1543.     Result := FmtStr2(sInst, sr1, ExtractFileName(GetArgument(2)));
  1544.   end;
  1545. end;
  1546.  
  1547. (*--------------------------------------------------------------------------*)
  1548.  
  1549. function GetRunCheckBoxText: string;
  1550. begin
  1551.   Result := GetRunString(SFXString(SFX_Msg_RunCheckBox_Run),
  1552.     SFXString(SFX_Msg_RunCheckBox_Inst));
  1553. end;
  1554.  
  1555. (*--------------------------------------------------------------------------*)
  1556.  
  1557. // get an error message if ExcuteCMD failed
  1558. function GetRunErrorMessage: string;
  1559. begin
  1560.   Result := GetRunString(SFXString(SFX_Err_Run_Run), SFXString(SFX_Err_Run_Inst));
  1561. end;
  1562.  
  1563. (*--------------------------------------------------------------------------*)
  1564.  
  1565.  
  1566. procedure ProcessMessages;
  1567. var
  1568.   Msg: TMsg;
  1569. begin
  1570.  { PeekMessage(Msg, 0, 0, 0, PM_REMOVE);
  1571.   TranslateMessage(Msg);
  1572.   DispatchMessage(Msg); }
  1573.   while (PeekMessage(msg, 0, 0, 0, PM_REMOVE)) do
  1574.   begin
  1575.     if not IsDialogMessage(0, msg) then
  1576.     begin
  1577.       TranslateMessage(msg);
  1578.       DispatchMessage(msg);
  1579.     end;
  1580.   end;
  1581. end;
  1582.  
  1583. (*--------------------------------------------------------------------------*)
  1584.  
  1585. procedure Make_CRC32Table;
  1586. var
  1587.   i, j: word;
  1588.   r: cardinal;
  1589. const
  1590.   CRCPOLY  = $EDB88320;
  1591.   UCHAR_MAX = 255;
  1592.   CHAR_BIT = 8;
  1593. begin
  1594.   for i := 0 to UCHAR_MAX do
  1595.   begin
  1596.     r := i;
  1597.     for j := CHAR_BIT downto 1 do
  1598.       if (r and 1) > 0 then
  1599.         r := (r shr 1) xor CRCPOLY
  1600.       else
  1601.         r := r shr 1;
  1602.     VArr_CRC32Table[i] := r;
  1603.   end;
  1604. end;
  1605.  
  1606. (*--------------------------------------------------------------------------*)
  1607.  
  1608. // Update the encryption keys with the next byte of plain text
  1609.  
  1610. procedure UpdateKeys(c: byte);
  1611. begin
  1612.   VArr_CryptKey[0] := UpdC32(c, VArr_CryptKey[0]);
  1613.   VArr_CryptKey[1] := VArr_CryptKey[1] + VArr_CryptKey[0] and $000000FF;
  1614.   VArr_CryptKey[1] := VArr_CryptKey[1] * 134775813 + 1;
  1615.   VArr_CryptKey[2] := UpdC32(HIBYTE(HIWORD(VArr_CryptKey[1])), VArr_CryptKey[2]);
  1616. end;
  1617.  
  1618. (*--------------------------------------------------------------------------*)
  1619.  
  1620. // Initialize the encryption keys and the random header according to the given password.
  1621.  
  1622. procedure seedk(passwd: AnsiString);
  1623. var
  1624.   i: byte;
  1625. begin
  1626.   VArr_CryptKey[0] := 305419896;
  1627.   VArr_CryptKey[1] := 591751049;
  1628.   VArr_CryptKey[2] := 878082192;
  1629.   for i := 1 to LENGTH(passwd) do
  1630.     UpdateKeys(byte(passwd[i]));
  1631. end;
  1632.  
  1633. (*--------------------------------------------------------------------------*)
  1634.  
  1635. // Return the next byte in the pseudo-random sequence
  1636.  
  1637. function decrypt_byte: integer;
  1638. var
  1639.   temp: word;
  1640. begin
  1641.   temp := word(VArr_CryptKey[2] or 2);
  1642.   Result := integer(word((temp * (temp xor 1)) shr 8) and $FF);
  1643. end;
  1644.  
  1645. (*--------------------------------------------------------------------------*)
  1646.  
  1647. function decrypt_pw(Encrypt_Head: PAnsiChar; EncHead_len: byte; BitFlag: word;
  1648.     CRC, FileDate: longint; const sPassword: AnsiString): boolean;
  1649. var
  1650.   i, c, b: byte;
  1651. begin
  1652.   Result := False;
  1653.   if sPassword = '' then
  1654.     Exit;
  1655.   seedk(sPassword);
  1656.   for i := 0 to EncHead_len - 1 do
  1657.   begin
  1658.     c := byte(Encrypt_Head[i + EncHead_len]) xor decrypt_byte;
  1659.     UpdateKeys(c);
  1660.     Encrypt_Head[i] := AnsiChar(c);
  1661.   end;
  1662.  
  1663.   (* version 2.0+ *)
  1664.   b := byte(Encrypt_Head[EncHead_len - 1]);
  1665.  
  1666.   if not ((BitFlag and 8) = 8) then
  1667.   begin
  1668.     if b = HIBYTE(HIWORD(crc)) then
  1669.       Result := True;
  1670.   end
  1671.   else
  1672.   begin
  1673.     if b = LOWORD(FileDate) shr 8 then
  1674.       Result := True;
  1675.   end;
  1676. end;
  1677.  
  1678. (*--------------------------------------------------------------------------*)
  1679.  
  1680.  // added october 10, 1998
  1681.  // enable/disable all children of the given parent window
  1682.  // this is used to disable all main dialog's controls during archive extraction
  1683.  // thanks to David - Kazuya david-kazuya@usa.net for report
  1684.  
  1685. procedure EnableChildren(const wnd: HWND; const bEnable: boolean);
  1686.  
  1687.   function FindChE(wnd: HWND; lParam: LPARAM): Bool; stdcall;
  1688.   var
  1689.     pCH: array[0..64] of char;
  1690.   begin
  1691.     Result := True;
  1692.     GetClassName(wnd, @pCH, 63);
  1693.     if IsWindowVisible(wnd) and (pCH <> 'msctls_progress32') then
  1694.       EnableWindow(wnd, boolean(lParam));
  1695.   end;
  1696.  
  1697. begin
  1698.   EnumChildWindows(wnd, @FindChE, integer(bEnable));
  1699. end;
  1700.  
  1701. (*--------------------------------------------------------------------------*)
  1702.  
  1703. // resize dialog/control
  1704.  
  1705. procedure ResizeControl(const wnd: HWND; const bReposition: boolean; yDiff: integer);
  1706. var
  1707.   pl: TWindowPlacement;
  1708. begin
  1709.   yDiff := MulDiv(yDiff, HIWORD(GetDialogBaseUnits), 8);
  1710.   pl.length := sizeof(pl);
  1711.   GetWindowPlacement(wnd, @pl);
  1712.   if bReposition then
  1713.     pl.rcNormalPosition.Top :=
  1714.       pl.rcNormalPosition.Top + yDiff;
  1715.   pl.rcNormalPosition.Bottom := pl.rcNormalPosition.Bottom + yDiff;
  1716.   SetWindowPlacement(wnd, @pl);
  1717. end;
  1718.  
  1719. (*--------------------------------------------------------------------------*)
  1720.  
  1721.  // from Angus Johnson's TZip-SFX code:
  1722.  // get the executable's file size to get rid of caring about the exe size
  1723. function GetExeSize: cardinal;
  1724. {$ifdef DEBUG_SFX}
  1725. begin
  1726.   Result := Test_Stub_Size;
  1727. end;
  1728. {$else}
  1729. var
  1730.   p: PByte;
  1731.   i, NumSections: integer;
  1732. const
  1733.   IMAGE_PE_SIGNATURE = $00004550;
  1734. begin
  1735.   Result := 0;
  1736.   p := pointer(hinstance);
  1737.   if (PImageDosHeader(p).e_magic <> IMAGE_DOS_SIGNATURE) then
  1738.     exit;
  1739.   Inc(p, PImageDosHeader(p)._lfanew);
  1740.   if  (PCardinal(p)^ <> IMAGE_PE_SIGNATURE) then
  1741.     exit;
  1742.   Inc(p, sizeof(cardinal));
  1743.   NumSections := PImageFileHeader(p).NumberOfSections;
  1744.   Inc(p, sizeof(TImageFileHeader) + sizeof(TImageOptionalHeader));
  1745.   for i := 1 to NumSections do
  1746.   begin
  1747.     with PImageSectionHeader(p)^ do
  1748.       if PointerToRawData + SizeOfRawData > Result then
  1749.         Result := PointerToRawData + SizeOfRawData;
  1750.     Inc(p, sizeof(TImageSectionHeader));
  1751.   end;
  1752. end;
  1753. {$endif}
  1754. (*--------------------------------------------------------------------------*)
  1755.  
  1756. const
  1757.   MAX_IDX = ($80000000 div sizeof(TZ64CentralEntry)) - 1; // 2G storage limit
  1758.  // storage for records in list view
  1759. type
  1760.   PCentralRecords = ^TCentralRecords;
  1761.   TCentralRecords = packed array[0..MAX_IDX] of TZ64CentralEntry;
  1762.  
  1763. var
  1764.   p_Items:  PCentralRecords = nil;
  1765.   cb_Items: cardinal = 0;
  1766.  
  1767. function AddToItemData(const rec: TZ64CentralEntry): cardinal;
  1768. begin
  1769.   if (cb_Items and 63) = 0 then
  1770.     ReAllocMem(p_Items, sizeof(TZ64CentralEntry) * (cb_Items + 64));
  1771.  
  1772.   Inc(cb_Items);
  1773.   p_Items^[cb_Items - 1] := rec;
  1774.   Result := cb_Items - 1;
  1775. end;
  1776.  
  1777. // add an entry to the list view
  1778. procedure AddFileToList(const wndOwner: HWND; const sName: string;
  1779.   const Rec: TZ64CentralEntry; const IsDir: boolean);
  1780. var
  1781.   recItem: TLVItem;
  1782.   wndLV: HWND;
  1783.   iiItem: integer;
  1784.   sfi: TSHFileInfo;
  1785.   s: string;
  1786. begin
  1787.   wndLV := GetDlgItem(wndOwner, ID_LV_FILES);
  1788.   if not IsDir then
  1789.     SHGetFileInfo(PChar(ExtractFileName(sName)), FILE_ATTRIBUTE_NORMAL, sfi,
  1790.       sizeof(sfi), SHGFI_USEFILEATTRIBUTES or SHGFI_SMALLICON or SHGFI_SYSICONINDEX)
  1791.   else
  1792.     SHGetFileInfo(PChar(ExtractFileName(RemoveDirSeparator(sName))),
  1793.       FILE_ATTRIBUTE_DIRECTORY, sfi, sizeof(sfi), SHGFI_USEFILEATTRIBUTES or
  1794.       SHGFI_SMALLICON or SHGFI_SYSICONINDEX);
  1795.  
  1796.   with recItem do
  1797.   begin
  1798.     mask  := LVIF_TEXT or LVIF_PARAM or LVIF_STATE or LVIF_IMAGE;
  1799.     iItem := MaxInt;
  1800.     iSubItem := 0;
  1801.     // select only if no file selection is allowed (just for visual feedback)
  1802.     if (so_AskFiles and VRec_SFXHeader.Options) = 0 then
  1803.       state := 0
  1804.     else
  1805.       state := LVIS_SELECTED;
  1806.     stateMask := LVIS_SELECTED;
  1807.     pszText := PChar(sName);
  1808.     cchTextMax := Length(sName);
  1809.     iImage  := sfi.iIcon;
  1810.     lParam  := AddToItemData(Rec);
  1811.   end;
  1812.   iiItem := SendMessage(wndLV, LVM_INSERTITEM, 0, integer(@recItem));
  1813.  
  1814.   with recItem do
  1815.   begin
  1816.     mask  := LVIF_TEXT;// or LVIF_PARAM;
  1817.     iItem := iiItem;
  1818.     iSubItem := 1;
  1819.     s := Int2Str(Rec.UnComprSize, 0);
  1820.     pszText := PChar(s);
  1821.     cchTextMax := Length(s);
  1822.   end;
  1823.   SendMessage(wndLV, LVM_SETITEM, 0, integer(@recItem));
  1824. end;
  1825.  
  1826. (*--------------------------------------------------------------------------*)
  1827.  
  1828. // retrieve an item from the list
  1829. function GetFileFromList(const wndOwner: HWND; const iiItem: integer;
  1830.   var Rec: TZ64CentralEntry): string;
  1831. var
  1832.   Item:  TLVItem;
  1833.   wndLV: HWND;
  1834.   szBuf: array[0..MAX_PATH * 2] of char;
  1835. begin
  1836.   wndLV := GetDlgItem(wndOwner, ID_LV_FILES);
  1837.  
  1838.   // get lparam stored in routine above
  1839.   with Item do
  1840.   begin
  1841.     iItem := iiItem;
  1842.     iSubItem := 0;
  1843.     mask  := LVIF_PARAM;
  1844.   end;
  1845.   SendMessage(wndLV, LVM_GETITEM, 0, integer(@Item));
  1846.   Rec := p_Items^[Item.lParam];
  1847.  
  1848.   // path+file
  1849.   with Item do
  1850.   begin
  1851.     iItem := iiItem;
  1852.     iSubItem := 0;
  1853.     mask  := LVIF_TEXT;
  1854.     pszText := @szBuf;
  1855.     cchTextMax := sizeof(szBuf);
  1856.   end;
  1857.   SetString(Result, Item.pszText, SendMessage(wndLV, LVM_GETITEMTEXT,
  1858.     iiItem, integer(@Item)));
  1859. end;
  1860.  
  1861. (*--------------------------------------------------------------------------*)
  1862.  
  1863. procedure DeSelectInFilesListView(const wndDlg: HWND; const iItem: integer);
  1864. var
  1865.   Item: TLVItem;
  1866. begin
  1867.   with Item do
  1868.   begin
  1869.     stateMask := LVIS_SELECTED;
  1870.     state := 0;
  1871.   end;
  1872.   SendDlgItemMessage(wndDlg, ID_LV_FILES, LVM_SETITEMSTATE, iItem, longint(@Item));
  1873. end;
  1874.  
  1875. (*--------------------------------------------------------------------------*)
  1876.  
  1877. // may 11, 2002: show first selected item on extraction failure
  1878. procedure ShowFirstSelected(const wndList: HWND);
  1879. var
  1880.   i: integer;
  1881. begin
  1882.   for i := 0 to Pred(SendMessage(wndList, LVM_GETITEMCOUNT, 0, 0)) do
  1883.     if SendMessage(wndList, LVM_GETITEMSTATE, i, LVIS_SELECTED) = LVIS_SELECTED then
  1884.     begin
  1885.       SendMessage(wndList, LVM_ENSUREVISIBLE, i, integer(False));
  1886.       Break;
  1887.     end;
  1888. end;
  1889.  
  1890. // check whether spanned or multivol archive
  1891. procedure CheckSpan;
  1892. var
  1893.   len: integer;
  1894.   sName: string;
  1895. begin
  1896.   // prepare name using defaults unless supplied
  1897.   if VStr_DetachName = '' then
  1898.     VStr_DetachName := ExtractFileName(VStr_ExeName);
  1899.   len := Length(VStr_DetachName);
  1900.   while (len > 0) and (VStr_DetachName[len] <> '.') do
  1901.     dec(len);
  1902.   if (len > 0) then
  1903.   begin
  1904.     if VInt_SpanType = SFXSpanTypeUnknown then
  1905.       VStr_DetachExt := Copy(VStr_DetachName, len, 255)
  1906.     else
  1907.       VStr_DetachExt := '.zip';
  1908.     VStr_DetachName := Copy(VStr_DetachName, 1, len -1);
  1909.   end
  1910.   else
  1911.     VStr_DetachExt := '.zip';
  1912.     VStr_DetachName := AppendDirSeparator(ExtractFilePath(VStr_ExeName)) +
  1913.             VStr_DetachName;
  1914.   VInt_SpanType := SFXSpanTypeMultiVol;
  1915.     VStr_SourceDir := AppendDirSeparator(ExtractFilePath(VStr_ExeName));
  1916.     if VStr_SourceDir = '' then
  1917.     begin
  1918.       len := GetCurrentDirectory(0, nil);
  1919.       if len > 0 then
  1920.       begin
  1921.         SetLength(sName, len + 5);
  1922.         GetCurrentDirectory(len + 2, pChar(sName));
  1923.         VStr_SourceDir := AppendDirSeparator(pChar(sName));
  1924.       end;
  1925.     end;
  1926.     VBool_FixedDrive := GetDriveType(PChar(VStr_SourceDir)) in
  1927.       [DRIVE_FIXED, DRIVE_REMOTE, DRIVE_RAMDISK];
  1928.     if not VBool_FixedDrive then
  1929.     begin
  1930.       sName := DetachedName('001');
  1931.       if not FileExists(sName) then
  1932.         VInt_SpanType := SFXSpanTypeSpanned;
  1933.     end;
  1934. end;
  1935.  
  1936. (*----------------------------------------------------------------------------
  1937. 3.3.1.0 11 Aug 2007 RA difference for dir and nondir in AddFileTo List addded
  1938. 3.3.0.0 24 Jan 2006 RA soCreateEmptyDirs added
  1939.   fill the list view
  1940. *)
  1941. procedure FillListView(wndOwner: hWnd);
  1942. type
  1943.   PUString_Data = ^UString_Data;
  1944.   UString_Data = packed record
  1945. //    tag: word;
  1946. //    totsiz: word;
  1947.     version: byte;
  1948.     origcrc: DWORD;
  1949.   end;
  1950. const
  1951.   PKZIPW25: Integer = 25;//(FS_FAT * 256) + 25;
  1952.   PKZIPW26 = 26;//(FS_FAT * 256) + 26;
  1953.   PKZIPW40 = 40;//(FS_FAT * 256) + 40;
  1954.   UNIXATTRS = $FFFF0000;
  1955.   WZIP = $0B32;//(FS_NTFS * 256) + 50;
  1956.   //  FS_FAT: Integer = 0;
  1957.   //  FS_HPFS: Integer = 6;
  1958.   //  FS_NTFS: Integer = 11;
  1959.   //  FLAG_UTF8_BIT = $1000;
  1960. var
  1961.   eoc: TZipEndOfCentral;
  1962.   i, j: cardinal;
  1963.   cfh: TZipCentralHeader;
  1964.   buffer: array [0..MAX_PATH + 2] of AnsiChar;
  1965.   fn: string;
  1966.   p: PByte;
  1967.   x: integer;
  1968.   fnp: PAnsiChar; // source filename pointer;
  1969.   fnsz: integer;  // source filename size
  1970.   fncp: integer;  // source filename codepage
  1971.  
  1972.   EOC64:  TZipEOC64;
  1973.   Z64CFH: TZ64CentralEntry;
  1974.   EocPos, CenSize, TotalEntries: int64;
  1975.   HasEoc64: boolean;        
  1976.   over, sz: integer;
  1977.   crc: cardinal;
  1978.   pp:  PUString_Data;
  1979.   BadName: boolean;
  1980.   hasUPath: Boolean;
  1981. begin
  1982.   EocPos := FSeek(FindEOCRecord, FILE_BEGIN);
  1983.  
  1984.   CheckFRead(eoc, sizeof(eoc));
  1985.   HasEoc64 := NeedEOC64(EOC);
  1986.   if (HasEoc64) then
  1987.     GetEOC64(EocPos, EOC64);
  1988.   //how far out the header offsets are from reality (due to sfx stub)
  1989.   if HasEoc64 and (EOC.CentralSize = MAX_UNSIGNED) then
  1990.     censize := EOC64.CentralSize
  1991.   else
  1992.     censize := EOC.CentralSize;//          EOC64.CentralSize : EOC.CentralSize;
  1993.   VDW_OffsetDelta := EocPos - censize;
  1994.   if HasEoc64 and (EOC.CentralOffSet = MAX_UNSIGNED) then
  1995.     VDW_OffsetDelta := VDW_OffsetDelta - EOC64.CentralOffSet
  1996.   else
  1997.     VDW_OffsetDelta := VDW_OffsetDelta - EOC.CentralOffSet;
  1998.   if (HasEoc64) then
  1999.     VDW_OffsetDelta := VDW_OffsetDelta - EOC64.vsize + 12 + SizeOf(TZip64EOCLocator);
  2000.   if HasEOC64 then
  2001.     censize := censize + EOC64.vsize + 12
  2002.   else
  2003.     censize := censize + SizeOf(EOC);
  2004.   FSeek(-censize, FILE_CURRENT);
  2005.   TotalEntries := EOC.TotalEntries;
  2006.   if (HasEoc64 and (TotalEntries = MAX_WORD)) then
  2007.     TotalEntries := EOC64.TotalEntries;
  2008.  
  2009.   // is it multi-disk
  2010.   if EOC.ThisDiskNo <> 0 then
  2011.     CheckSpan;
  2012.  
  2013.   //how far out the header offsets are from reality (due to sfx stub)
  2014.   for i := 0 to TotalEntries - 1 do
  2015.     begin
  2016.       CheckFRead(cfh, sizeof(cfh));
  2017.       if (cfh.HeaderSig <> ZipCentralHeaderSig) or (cfh.FileNameLen = 0) or
  2018.          (cfh.FileNameLen > 500) then
  2019.         ErrorHaltID(SFX_Err_ArchiveCorrupted);
  2020.  
  2021.       CheckFRead(buffer[0], cfh.FileNameLen);
  2022.       buffer[cfh.FileNameLen] := #0;
  2023.       // read extra data
  2024.       p := nil;
  2025.       over := 0;
  2026.       x := cfh.ExtraLen;
  2027.       if x > 0 then
  2028.       begin
  2029.         if x > 2048 then
  2030.         begin
  2031.           over := 2048 - x;
  2032.           x := 2048;
  2033.         end;
  2034.         p := GetXBuf(x);
  2035.         if p <> nil then
  2036.           xbuf^ := 0;
  2037.         if x > 0 then
  2038.           CheckFRead(p^, x);
  2039.       end;
  2040.       GetZ64Entry(cfh, Z64cfh);
  2041.       BadName := false;
  2042.       fnp := PAnsiChar(@buffer[0]);   // filename source
  2043.       fnsz := cfh.FileNameLen;//-1;       // filename source length
  2044.       fncp := 0;
  2045.       hasUPath := False;
  2046.  
  2047.       if (p <> nil) and (cfh.VersionMadeBy0 >= 20) then
  2048.       begin
  2049.         sz := cfh.ExtraLen;
  2050.         if ExtraData(p, sz, UPath_Data_Tag) and
  2051.           (sz > sizeof(UString_Data)) then
  2052.         begin
  2053.           pp  := PUString_Data(p);;
  2054.           crc := $FFFFFFFF;
  2055.           Crc32_Buf(@buffer[0], cfh.FileNameLen, crc);
  2056.           crc := crc xor $FFFFFFFF;
  2057.           if (pp^.version = 1) and (crc = pp^.origcrc) then
  2058.           begin
  2059.             sz := sz - sizeof(UString_Data);
  2060.             inc(p, sizeof(UString_Data));
  2061.             if sz > 0 then
  2062.             begin
  2063.               fnp := PAnsiChar(p);
  2064.               fnsz := sz;
  2065.               fncp := CP_UTF8;
  2066.               hasUPath := True;
  2067.             end;
  2068.           end;
  2069.         end;
  2070.       end;
  2071.  
  2072.       if not hasUPath then
  2073.       begin
  2074.         fncp := CP_ACP;
  2075.         if (cfh.Flag and FLAG_UTF8_BIT) <> 0 then
  2076.           fncp := CP_UTF8
  2077.         else
  2078.         begin
  2079.           if (cfh.VersionMadeBy1 = FS_FAT) or
  2080.               (cfh.VersionMadeBy1 = FS_HPFS) or
  2081.               ((cfh.VersionMadeBy1 = FS_NTFS) and (cfh.VersionMadeBy0 = 50)) then
  2082.               fncp := CP_OEMCP;
  2083.           end;
  2084.       end;
  2085.  
  2086.       fn := To_Str(fncp, fnp, fnsz, true, BadName);
  2087.         //swap slashes and get last char ...
  2088.       for J := 1 to Length(fn) do
  2089.       begin
  2090.         if fn[j] = '/' then
  2091.           fn[j] := Chr_DirSep;
  2092.         if fn[j] = '?' then
  2093.           BadName := True;
  2094.       end;
  2095.  
  2096.       if BadName then
  2097.       begin
  2098.         fn := SFXString(SFX_Err_InvalidFileName) + ' "' + fn + '"';
  2099.         x := MessageBox(wndOwner, pChar(fn), PChar(SFXString(SFX_Cap_Err)),
  2100.                 MB_OKCANCEL or MB_ICONSTOP or MB_TASKMODAL);
  2101.         if x = IDOK then
  2102.           continue;
  2103.         break;
  2104.       end;
  2105.  
  2106.       // skip directory entries
  2107.       if fn[Length(fn)] <> Chr_DirSep then
  2108.       begin
  2109.         //store each filename and absolute file offset of cfh record ...
  2110.         AddFileToList(wndOwner, fn, Z64cfh, False);
  2111.       end
  2112.       else
  2113.       // new 09/19/2005, recreate empty directories
  2114.       if (so_CreateEmptyDirs and VRec_SFXHeader.Options) <> 0 then
  2115.         AddFileToList(wndOwner, fn, Z64cfh, True);
  2116.  
  2117.       if (over + cfh.FileComLen) <> 0 then
  2118.         FSeek(over + cfh.FileComLen, FILE_CURRENT);
  2119.     end;
  2120.  
  2121. end;
  2122.  
  2123. (*--------------------------------------------------------------------------*)
  2124.  
  2125. // close a handle, if not already closed
  2126. function CheckCloseHandle(var H: THandle): boolean;
  2127. begin
  2128.   if (H <> 0) and (H <> INVALID_HANDLE_VALUE) then
  2129.     Result := CloseHandle(H)
  2130.   else
  2131.     Result := True;
  2132.   H := INVALID_HANDLE_VALUE;
  2133. end;
  2134.  
  2135. // create a 00x number string
  2136. procedure Str_3(const i: integer; var S: string);
  2137. begin
  2138.   S := Int2Str(i, 3);
  2139. //  Str(i, S);
  2140. //  while Length(s) < 3 do
  2141. //    s := '0' + s;
  2142. end;
  2143.  
  2144. function IsRightDisk(DiskSeq: integer): boolean;
  2145. var
  2146.   SSeq:  string;
  2147.   Dummy1, Dummy2, DiskSerial: cardinal;
  2148.   VolName: array[0..MAX_PATH] of char;
  2149.   sTemp: string;
  2150. begin
  2151.   Result := DiskSeq = VInt_LastSeq;
  2152.   if Result then
  2153.     exit;
  2154.   Str_3(DiskSeq + 1, SSeq);
  2155.   if VInt_SpanType = SFXSpanTypeSpanned then
  2156.   begin
  2157.     // get volume info
  2158.     GetVolumeInformation(PChar(VStr_SourceDir), VolName, MAX_PATH, @DiskSerial, Dummy1,
  2159.       Dummy2, nil, 0);
  2160.     STemp  := VolName;
  2161.     // must be pkback# 00x
  2162.     Result := CompareText(STemp, 'PKBACK# ' + SSeq);
  2163.     if Result and (not CompareText(VStr_ExeName, DetachedName(''))) then
  2164.     begin
  2165.       if not CheckCloseHandle(VH_InFile) then
  2166.         ErrorHaltFmt(SFX_Err_CannotCloseFile, VStr_ExeName);
  2167.       VStr_ExeName := DetachedName(''); // use detached name
  2168.       // Open the input archive on this disk.
  2169.       VH_Infile := CreateFile(PChar(VStr_ExeName), GENERIC_READ,
  2170.         FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  2171.       if VH_Infile = INVALID_HANDLE_VALUE then
  2172.         ErrorHaltFmt(SFX_Err_CannotOpenFile, VStr_ExeName);
  2173.       // assume no shifted offset on detached archives
  2174.       VDW_OffsetDelta := 0;
  2175.     end;
  2176.   end
  2177.   else
  2178.   begin
  2179.     // multi volume, filename = xyz00x.(xyz) and Actual File
  2180.     Result := CompareText(VStr_ExeName, DetachedName(SSeq));
  2181.   end;
  2182. end;
  2183.  
  2184. procedure GetNewDisk(wndOwner: HWND; DiskSeq: integer);
  2185. var
  2186.   SSeq: string;
  2187. begin
  2188.   if not CheckCloseHandle(VH_InFile) then
  2189.     ErrorHaltFmt(SFX_Err_CannotCloseFile, VStr_ExeName);
  2190.  
  2191.   Str_3(DiskSeq + 1, SSeq);
  2192.   repeat
  2193.       if not VBool_FixedDrive then
  2194.       begin
  2195.         if MsgBox(wndOwner, FmtStr2(SFXString(SFX_Msg_InsertDiskVolume),
  2196.           SSeq, VStr_SourceDir), VStr_SFX_Caption, MB_OKCANCEL) = idCancel then
  2197.           ErrorHalt('');
  2198.       end;
  2199.  
  2200.       if VInt_SpanType = SFXSpanTypeMultiVol then
  2201.         VStr_ExeName := DetachedName(SSeq)
  2202.       else
  2203.         VStr_ExeName := DetachedName('');
  2204.   until IsRightDisk(DiskSeq);
  2205.  
  2206.   // Open the input archive on this disk.
  2207.   VH_Infile := CreateFile(PChar(VStr_ExeName), GENERIC_READ, FILE_SHARE_READ,
  2208.     nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  2209.   if VH_Infile = INVALID_HANDLE_VALUE then
  2210.     ErrorHaltFmt(SFX_Err_CannotOpenFile, VStr_ExeName);
  2211.   // assume no shifted offset on detached archives
  2212.   VDW_OffsetDelta := 0;
  2213.   VInt_LastSeq := DiskSeq;
  2214. end;
  2215.  
  2216.  
  2217. (*----------------------------------------------------------------------------
  2218.  added version 3.0 May 1, 2003
  2219.  read data and copy in temp file if needed or skip to next local header *)
  2220. procedure RWJoinData(wndOwner: HWND; var Buffer; ReadLen: integer;
  2221.   var DiskNbr: word; Copy: boolean);
  2222. var
  2223.   SizeR, ToRead: integer;
  2224. begin
  2225.   while ReadLen > 0 do
  2226.   begin
  2227.     ToRead := min(ReadLen, SFXBufSize);
  2228.     SizeR  := FRead(Buffer, ToRead);
  2229.     if SizeR <> ToRead then
  2230.     begin
  2231.       // Check if we are at the end of a input disk.
  2232.       if (VInt_SpanType = SFXSpanTypeNone) or
  2233.           (FSeek(0, FILE_CURRENT) <> FSeek(0, FILE_END)) then
  2234.         ErrorHaltID(SFX_Err_ArchiveCorrupted);
  2235.  
  2236.       // It seems we are at the end, so get a next disk.
  2237.       Inc(DiskNbr);
  2238.       GetNewDisk(wndOwner, DiskNbr);
  2239.     end;
  2240.  
  2241.     if SizeR > 0 then
  2242.     begin
  2243.       if Copy then
  2244.         CheckFWrite(VH_TempFile, Buffer, SizeR, VStr_TempFile);
  2245.       ReadLen := ReadLen - SizeR;
  2246.     end;
  2247.   end;
  2248. end;
  2249.  
  2250. // open the correct archive in spanned, multivolue or detached sfx's
  2251. procedure OpenRightArchive(wndOwner: HWND; const DiskNumber: integer);
  2252. begin
  2253.   if not IsRightDisk(DiskNumber) then
  2254.     GetNewDisk(wndOwner, DiskNumber); // we need another disk
  2255. end;
  2256.  
  2257.  
  2258.  
  2259. // spanned archive, extract local header and file data to a temporary file
  2260. procedure ExtractToTempFile(const wndOwner: HWND; var LocalOffset: cardinal;
  2261.   var OldHandle: THandle);
  2262. var
  2263.   Buf: array[0..SFXBufSize] of Char;
  2264.   DataToCopy: cardinal;
  2265. begin
  2266.   if VStr_TempFile = '' then
  2267.   begin
  2268.     ZeroMemory(@buf, sizeof(buf));
  2269.     // create a temporaray filename
  2270.     SetLength(VStr_TempFile, MAX_PATH * 2);
  2271.     if GetTempFileName(PChar(AppendDirSeparator(ExpandEnv('%temp%'))),
  2272.       'SFX', 0, Buf) <> 0 then
  2273.     begin
  2274.       VStr_TempFile := buf;
  2275.       DeleteFile(buf); // because created by GetTempFileName
  2276.     end
  2277.     else
  2278.       ErrorHaltFmt(SFX_Err_CannotOpenFile, VStr_TempFile);
  2279.   end;
  2280.  
  2281.   // create temp file to copy the deflated file from the archive and prepare it for inflate
  2282.   VH_TempFile := CreateFile(PChar(VStr_TempFile), GENERIC_READ or
  2283.     GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, 0);
  2284.  
  2285.   if VH_TEMPFILE = INVALID_HANDLE_VALUE then
  2286.     ErrorHaltFmt(SFX_Err_CannotWriteFile, VStr_TempFile);
  2287.  
  2288.   FSeek(LocalOffset, FILE_BEGIN);
  2289.   // read the local header        
  2290.   VInt_MaxWrite  := sizeof(buf); // do not allow overlength
  2291.   RWJoinData(wndOwner, Buf, sizeof(TZipLocalHeader), VRec_ZipHeader.DiskStart,
  2292.     True);
  2293.   with PZipLocalHeader(@Buf)^ do
  2294.   begin
  2295.     if HeaderSig <> ZipLocalHeaderSig then
  2296.       BadArchive;
  2297.     DataToCopy := VRec_ZipHeader.ComprSize + FileNameLen + ExtraLen;
  2298.     // ext local header?
  2299.     if (Flag and 8) = 8 then
  2300.       DataToCopy := DataToCopy + sizeof(TZipExtLocalHeader);
  2301.   end;                                
  2302.   VInt_MaxWrite  := DataToCopy; // do not allow overlength
  2303.   RWJoinData(wndOwner, Buf, DataToCopy, VRec_ZipHeader.DiskStart, True);
  2304.   OldHandle := VH_InFile;
  2305.   VH_InFile := VH_TempFile;
  2306.   LocalOffset := 0;
  2307. end;
  2308.  
  2309. // reattach current archive and close tempfile
  2310. procedure RollBackTempFile(const wndOwner: HWND; const OldHandle: THandle);
  2311. begin
  2312.   VH_InFile := OldHandle;
  2313.   if not CheckCloseHandle(VH_TempFile) then
  2314.     ErrorMsgBoxFmt1(wndOwner, SFX_Err_CannotCloseFile, VStr_TempFile);
  2315.   DeleteFile(PChar(VStr_TempFile));
  2316. end;
  2317.  
  2318. function ExtractFile(wndOwner: hWnd; const Filename: string;
  2319.   const rec: TZ64CentralEntry; var bPasswordFailed: boolean): boolean;
  2320. var
  2321.   EncryptHDR: PAnsiChar;
  2322.   i: integer;
  2323.   rLocal: TZipLocalHeader;
  2324.   bIsTempFile: boolean;
  2325.   clOffset: cardinal;
  2326.   oldH: THandle;
  2327. begin
  2328.   bPasswordFailed := False;
  2329.   bIsTempFile := False;
  2330.   Result := False;
  2331.   // mst may 07, 2002: removed a sleep(100) where did this come from?
  2332.   VRec_ZipHeader := rec;
  2333.   with VRec_ZipHeader do
  2334.   begin
  2335.     if HeaderSig <> ZipCentralHeaderSig then  
  2336.       BadArchive;
  2337.     if not (ComprMethod in [0, 8]) then                        
  2338.       ErrorMsgBoxFmt1(wndOwner, SFX_Err_ZipUnknownComp, '')
  2339.     else
  2340.     begin
  2341.       clOffset := RelOffLocal + VDW_OffsetDelta;
  2342.       if (VInt_SpanType <> SFXSpanTypeNone{0}) then
  2343.       begin
  2344.         // assure the right disk is opened
  2345.         OpenRightArchive(wndOwner, DiskStart);
  2346.  
  2347.         // join the possibly split data in a temporary file
  2348.         ExtractToTempFile(wndOwner, clOffset, oldH);
  2349.         bIsTempFile := True;
  2350.       end;
  2351.  
  2352.       try
  2353.         // goto beginning of local header...
  2354.         FSeek(clOffset, FILE_BEGIN);
  2355.         CheckFRead(rLocal, sizeof(rLocal));
  2356.         if rLocal.HeaderSig <> ZipLocalHeaderSig then
  2357.           BadArchive;
  2358.  
  2359.         // mst may 07, 2002: added extrafieldlen to go to correct position
  2360.         // e.g. for zipfiles created with infozip's zip.exe
  2361.         FSeek(rLocal.FileNameLen + rLocal.ExtraLen, FILE_CURRENT);
  2362.         VInt_BytesToGo := ComprSize;
  2363.         VInt_MaxWrite  := UnComprSize; // do not allow overlength
  2364.  
  2365.         //password stuff...
  2366.         if (Flag and 1) = 1 then //if a password used...
  2367.         begin
  2368.           Dec(VInt_BytesToGo, RAND_HEAD_LEN);
  2369.           GetMem(EncryptHDR, RAND_HEAD_LEN * 2);
  2370.           try
  2371.             CheckFRead(EncryptHDR[0], RAND_HEAD_LEN);
  2372.             //make a copy of encrypted header in upper half of buffer...
  2373.             Move(EncryptHDR[0], EncryptHDR[RAND_HEAD_LEN], RAND_HEAD_LEN);
  2374.             if VStr_Password = '' then
  2375.               bPasswordFailed := True
  2376.             else
  2377.               bPasswordFailed :=
  2378.                 not decrypt_pw(EncryptHDR, RAND_HEAD_LEN, Flag,
  2379.                 CRC32, ModifTime, VStr_Password);
  2380.             if bPasswordFailed then
  2381.               for i := 0 to 2 do
  2382.               begin
  2383.                 if DialogBox(hInstance, Str_Dlg_Password, wndOwner,
  2384.                   @PasswordQueryDialogProc) <> idOk then
  2385.                   Break;
  2386.                 bPasswordFailed :=
  2387.                   not decrypt_pw(EncryptHDR, RAND_HEAD_LEN, Flag,
  2388.                   CRC32, ModifTime, VStr_Password);
  2389.                 if not bPasswordFailed then
  2390.                   Break;
  2391.                 Windows.Beep(0, 0); //it's a dud, ? try again...
  2392.               end;
  2393.           finally
  2394.             FreeMem(EncryptHDR);
  2395.           end;
  2396.         end;
  2397.  
  2398.         if bPasswordFailed then
  2399.           Exit;
  2400.  
  2401.         VDW_CRC32Val := CRC_MASK;
  2402.         ProcessMessages;
  2403.         if VBool_Cancelled then
  2404.           Exit;
  2405.  
  2406.         VStr_OutFile := FileName;
  2407.         VH_OutFile := CreateFile(PChar(Filename), GENERIC_WRITE,
  2408.           FILE_SHARE_WRITE, nil, CREATE_ALWAYS, ExtFileAtt and $7F, 0);
  2409.  
  2410.         if VH_OutFile = INVALID_HANDLE_VALUE then
  2411.         begin                        
  2412.           ErrorMsgBoxFmt1(wndOwner, SFX_Err_CannotWriteFile, VStr_OutFile);
  2413.           Exit;
  2414.         end;
  2415.  
  2416.         try
  2417.           case ComprMethod of
  2418.             0: Unstore;
  2419.             8: Inflate(nil, 0);
  2420.           end;
  2421.           // set file time
  2422.           if CTime = 0 then
  2423.             FileSetDate(VH_OutFile, ModifTime + 65536 * ModifDate)
  2424.           else
  2425.           begin
  2426.             SetFileTime(VH_OutFile, @CTime, @ATime, @MTime);
  2427.           end;
  2428.  
  2429.           // 01/13/04: do crc32 checking, bail a warning message
  2430.           //      but do not stop if checksums do not match
  2431.           if rec.CRC32 <> (VDW_CRC32Val xor $FFFFFFFF) then
  2432.           begin
  2433.             ErrorMsgBoxFmt1(wndOwner, SFX_Err_CRC32, VStr_OutFile);
  2434.             Result := False;
  2435.           end
  2436.           else
  2437.             Result := True;
  2438.  
  2439.         finally
  2440.           if not CheckCloseHandle(VH_OutFile) then
  2441.           begin                                            
  2442.             ErrorMsgBoxFmt1(wndOwner, SFX_Err_CannotCloseFile, VStr_OutFile);
  2443.             Result := False;
  2444.           end;
  2445.         end;
  2446.       finally
  2447.         if bIsTempFile then
  2448.           RollBackTempFile(wndOwner, oldH);
  2449.       end;
  2450.     end;
  2451.   end;
  2452. end;
  2453.  
  2454. (*--------------------------------------------------------------------------*)
  2455.  
  2456. function Extract(wndOwner: hWnd): boolean;
  2457. var
  2458.   i, FileCount: longint;
  2459.   wndList: HWND;
  2460.   wndProgressBar: HWND;
  2461.   bExtracted, bPWFailed: boolean;
  2462.   recCentral: TZ64CentralEntry;
  2463. begin
  2464.   wndList := GetDlgItem(wndOwner, ID_LV_FILES);
  2465.   wndProgressBar := GetDlgItem(wndOwner, ID_PRG_EXTRACT);
  2466.   FileCount := SendMessage(wndList, LVM_GETITEMCOUNT, 0, 0);
  2467.   SendMessage(wndProgressBar, PBM_SETRANGE, 0, FileCount shl 16);
  2468.   SendMessage(wndProgressBar, PBM_SETPOS, 0, 0);
  2469.  
  2470.   VInt_LastSeq := -1;
  2471.   bPWFailed  := False;
  2472.   bExtracted := False;
  2473.   for i := 0 to FileCount - 1 do
  2474.   begin
  2475.     ProcessMessages;
  2476.     if VBool_Cancelled then
  2477.       Break;
  2478.  
  2479.     // update progres bar
  2480.     SendMessage(wndProgressBar, PBM_SETPOS, i + 1, 0);
  2481.  
  2482.     if SendMessage(wndList, LVM_GETITEMSTATE, i, LVIS_SELECTED) = LVIS_SELECTED then
  2483.       //if selected then...
  2484.     begin
  2485.       //get the target filename...
  2486.       VStr_CurrentFile := AppendDirSeparator(VStr_ExtractPath) +
  2487.         GetFileFromList(wndOwner, i, recCentral);
  2488.  
  2489.       if (VStr_CurrentFile <> '') and
  2490.         (VStr_CurrentFile[Length(VStr_CurrentFile)] = Chr_DirSep) then
  2491.       begin
  2492.         if (so_CreateEmptyDirs and VRec_SFXHeader.Options) <> 0 then
  2493.         begin
  2494.           if not ForceDirectories(RemoveDirSeparator(VStr_CurrentFile)) then
  2495.           begin
  2496.             ErrorMsgBoxFmt1(wndOwner, SFX_Err_Directory,
  2497.               RemoveDirSeparator(VStr_CurrentFile));
  2498.             bExtracted := False;
  2499.           end
  2500.           else
  2501.             bExtracted := True;
  2502.         end
  2503.         else
  2504.           bExtracted := False;
  2505.       end
  2506.       else
  2507.       begin
  2508.         if not ForceDirectories(ExtractFilePath(VStr_CurrentFile)) then
  2509.         begin                                                        
  2510.           ErrorMsgBoxFmt1(wndOwner, SFX_Err_Directory,
  2511.               RemoveDirSeparator(VStr_CurrentFile));
  2512.           Break;
  2513.         end;
  2514.  
  2515.         if (Integer(VRec_SFXHeader.DefOVW) <> som_Overwrite) and
  2516.           FileExists(PChar(VStr_CurrentFile)) then
  2517.         begin
  2518.           if Integer(VRec_SFXHeader.DefOVW) = som_Skip then
  2519.             continue;
  2520.           case DialogBox(hInstance, Str_Dlg_FileExists, wndOwner,
  2521.               @FileExistsDialogProc) of
  2522.             ID_BTN_YES: ;
  2523.             ID_BTN_NO: continue;
  2524.           end;
  2525.         end;
  2526.  
  2527.         // make sure the correct zip archive is open
  2528.         if (VInt_SpanType <> SFXSpanTypeNone{0}) then
  2529.           OpenRightArchive(wndOwner, recCentral.DiskStart);
  2530.  
  2531.         bExtracted := ExtractFile(wndOwner, VStr_CurrentFile, recCentral,
  2532.           bPWFailed);
  2533.       end;
  2534.  
  2535.       if bPWFailed then
  2536.         break //stop further processing!!!
  2537.       else
  2538.       if bExtracted then
  2539.         //unselect the file if successfully extracted with no errors...
  2540.         DeSelectInFilesListView(wndOwner, i);
  2541.     end;
  2542.   end;
  2543.   Result := SendMessage(wndList, LVM_GETSELECTEDCOUNT, 0, 0) = 0;
  2544.   if not Result then
  2545.     ShowFirstSelected(wndList); //may 11, 2002 : better visual feedback
  2546. end;
  2547.  
  2548. (*--------------------------------------------------------------------------*)
  2549.  
  2550. procedure ErrorHaltFmt(id: integer; const arg1: string);
  2551. begin
  2552.   ErrorHalt(FmtStrID1(id, arg1));
  2553. end;
  2554.  
  2555. procedure ErrorHaltID(id: integer);
  2556. begin
  2557.   ErrorHalt(SFXString(id));
  2558. end;
  2559.  
  2560. // fatal error, exit
  2561. procedure ErrorHalt(const sMsg: string);
  2562. {$ifdef DEBUG_SFX}
  2563. var err: DWORD;  m: string;
  2564. begin
  2565.   err := GetLastError;
  2566.   m := sMsg;
  2567.   if err <> 0 then
  2568.     m := m + ' ['+ IntToHex(err, 8) + ' '+ SysErrorMessage(err)+']';
  2569.   if m <> '' then
  2570.     ErrorMsgBox(0, m{sMsg});
  2571.   raise Exception.Create('Program halted');
  2572. //  Halt(1);
  2573. end;
  2574. {$else}
  2575. begin
  2576.   if sMsg <> '' then
  2577.     ErrorMsgBox(0, sMsg);
  2578.   Halt(1);
  2579. end;
  2580. {$endif}
  2581. (*--------------------------------------------------------------------------*)
  2582.  
  2583. function StrGetEditText(wndPar: HWND): string;
  2584. begin
  2585.   SetLength(Result, GetWindowTextLength(GetDlgItem(wndPar, ID_EDITBOX)) * 2);
  2586.   if Result <> '' then
  2587.   begin
  2588.     GetDlgItemText(wndPar, ID_EDITBOX, PChar(Result), Length(Result));
  2589.     Result := PChar(Result); // match length
  2590.   end;
  2591. end;
  2592.  
  2593. procedure AddFilesListViewCol(const wndDlg: HWND; const iIndex: integer;
  2594.   const szCaption: string; const iDirection, iWidth: integer);
  2595. var
  2596.   wndLV:  HWND;
  2597.   recCol: TLVColumn;
  2598. begin
  2599.   wndLV := GetDlgItem(wndDlg, ID_LV_FILES);
  2600.   with recCol do
  2601.   begin
  2602.     mask := LVCF_FMT or LVCF_SUBITEM or LVCF_TEXT or LVCF_WIDTH;
  2603.     fmt := iDirection;
  2604.     cx := iWidth;
  2605.     pszText := PChar(szCaption);
  2606.     cchTextMax := Length(szCaption);
  2607.     iSubItem := iIndex;
  2608.   end;
  2609.   SendMessage(wndLV, LVM_INSERTCOLUMN, iIndex, integer(@recCol));
  2610. end;
  2611.  
  2612. procedure SelectAllInFilesListView(const wndDlg: HWND);
  2613. var
  2614.   Item: TLVItem;
  2615. begin
  2616.   with Item do
  2617.   begin
  2618.     stateMask := LVIS_SELECTED;
  2619.     state := LVIS_SELECTED;
  2620.   end;
  2621.   SendDlgItemMessage(wndDlg, ID_LV_FILES, LVM_SETITEMSTATE, -1, longint(@Item));
  2622. end;
  2623.  
  2624. // get current directory
  2625. function GetCurDir: string;
  2626. var
  2627.   szBuf: array[0..MAX_PATH] of char;
  2628. begin
  2629.   SetString(Result, szBuf, GetCurrentDirectory(MAX_PATH, szBuf));
  2630. end;
  2631.  
  2632. function Int2Str(n: int64; wide: integer = -1): String;
  2633. var
  2634.   rev: array[0..25] of Char;
  2635.   i, k: Integer;
  2636.   prev: PChar;
  2637. begin
  2638.   i := 0;
  2639.   prev := @rev[25];
  2640.   prev^ := #0;
  2641.   while n <> 0 do
  2642.   begin
  2643.     inc(i);
  2644.     dec(prev);
  2645.     k := n mod 10;
  2646.     prev^ := Char(Ord('0') + k);
  2647.     n := n div 10;
  2648.   end;
  2649.   while (i < wide) and (i < 24) do
  2650.   begin
  2651.     inc(i);
  2652.     dec(prev);
  2653.     prev^ := '0';
  2654.   end;
  2655.   Result := String(prev);
  2656. end;
  2657.  
  2658. // return the Detached name
  2659. function DetachedName(const num: string): string;
  2660. begin
  2661.   Result := VStr_DetachName;
  2662.   if num <> '' then
  2663.     Result := Result + num;
  2664.   Result := Result + VStr_DetachExt;
  2665. end;
  2666.  
  2667. function LoadResource(id: integer): Pointer;
  2668. var
  2669.   hFind, hRes: THandle;
  2670. Begin
  2671.   Result := nil;
  2672.   hFind := Windows.FindResource(HInstance, PChar(id), RT_RCDATA);
  2673.   if hFind <> 0 then
  2674.   begin
  2675.     hRes := Windows.LoadResource(HInstance, hFind);
  2676.     if hRes <> 0 then
  2677.       Result := Windows.LockResource(hRes);
  2678.   end;
  2679. End;
  2680.  
  2681.  
  2682. procedure Finish;
  2683. begin
  2684.   if xbuf <> nil then
  2685.     FreeMem(xbuf);
  2686. //  FreeMem(VRec_Langs);
  2687.   ReAllocMem(p_Items, 0);
  2688.   if not CheckCloseHandle(VH_InFile) then
  2689.     ErrorMsgBoxFmt1(0, SFX_Err_CannotCloseFile, VStr_ExeName);
  2690.   if not CheckCloseHandle(VH_TempFile) then
  2691.     ErrorMsgBoxFmt1(0, SFX_Err_CannotCloseFile, VStr_TempFile);
  2692.   if VStr_TempFile <> '' then
  2693.     DeleteFile(PChar(VStr_TempFile));
  2694.   if not CheckCloseHandle(VH_OutFile) then
  2695.     ErrorMsgBoxFmt1(0, SFX_Err_CannotCloseFile, VStr_OutFile);
  2696.   FreeMem(VRec_Strings);
  2697.   FreeMem(VRec_DefStrings);
  2698. end;
  2699.  
  2700. procedure Run;
  2701. var
  2702.   sfi: TSHFileInfo; // to get shell image list handle
  2703. {$IFNDEF DEBUG_SFX}
  2704.   pBuf: array[0..MAX_PATH] of Char; // buffer for paramstr(0)
  2705. {$ENDIF}
  2706.   sVar: string;
  2707.   CCInfo: TCCInitCommonControlsEx;
  2708. begin
  2709.   // may 11, 2002: added support for environment variable %TICKS%
  2710.   sVar := Int2Str(GetTickCount, 0);
  2711.   SetEnvironmentVariable('TICKS', PChar(sVar));
  2712.  
  2713.   // initialize common controls (for the progress bar and listview)
  2714.   CCInfo.dwICC := ICC_LISTVIEW_CLASSES or ICC_PROGRESS_CLASS or ICC_STANDARD_CLASSES;
  2715.   CCInfo.dwSize := sizeof(TCCInitCommonControlsEx);
  2716.   InitCommonControlsEx(@CCInfo);
  2717.  
  2718.   // Created in the initialisation section of SFXDialogs.pas
  2719.   Make_CRC32Table;
  2720.  
  2721.   // get default strings
  2722.   if LoadLang(VRec_DefStrings, SFX_LANG_BASE) <= 0 then
  2723.     ErrorHaltID(SFX_Err_Archive);
  2724.   VStr_SFX_Caption := SFXString(SFX_Cap_App);
  2725.   SetLanguage;
  2726.   VStr_SFX_Caption := SFXString(SFX_Cap_App); // may be diferent language
  2727.  
  2728. {$IFNDEF DEBUG_SFX}  
  2729.     // needs less code than ParamStr(0)
  2730.     SetString(VStr_ExeName, pBuf, GetModuleFileName(0, pBuf, sizeof(pBuf)));
  2731. {$ENDIF}
  2732.  
  2733.   // open the archive file (i myself!)
  2734.   VH_InFile := CreateFile(PChar(VStr_ExeName), GENERIC_READ, FILE_SHARE_READ, nil,
  2735.     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  2736.  
  2737.   //  If error, notify and abort
  2738.   if VH_InFile = INVALID_HANDLE_VALUE then
  2739.     ErrorHaltFmt(SFX_Err_Archive, VStr_ExeName);
  2740.  
  2741.   // read the TSFXFileHeader record (and the appended strings) from the file
  2742.   GetDefParams;
  2743.  
  2744.   // get the shell's image list handle
  2745.   FillChar(sfi, sizeof(sfi),0);
  2746.   VH_ShellImageList := SHGetFileInfo(PChar(VStr_ExeName), 0, sfi, sizeof(sfi),
  2747.       SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
  2748.  
  2749.   // Display the dialog
  2750.   DialogBox(hInstance, Str_Dlg_Main, 0, @MainDialogProc);
  2751. end;
  2752.  
  2753. {$ifndef DEBUG_SFX}
  2754. initialization
  2755.   VStr_SFX_Caption := '';//SFXString(SFX_Cap_App);
  2756.  
  2757. finalization
  2758.   // cleanup
  2759.   Finish;
  2760. {$endif}
  2761. end.
  2762.  
  2763.