Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | daniel-mar | 1 | unit ZMUtils19; |
2 | |||
3 | (* |
||
4 | ZMUtils19.pas - Some utility functions |
||
5 | Copyright (C) 2009, 2010 by Russell J. Peters, Roger Aelbrecht, |
||
6 | Eric W. Engler and Chris Vleghert. |
||
7 | |||
8 | This file is part of TZipMaster Version 1.9. |
||
9 | |||
10 | TZipMaster is free software: you can redistribute it and/or modify |
||
11 | it under the terms of the GNU Lesser General Public License as published by |
||
12 | the Free Software Foundation, either version 3 of the License, or |
||
13 | (at your option) any later version. |
||
14 | |||
15 | TZipMaster is distributed in the hope that it will be useful, |
||
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
18 | GNU Lesser General Public License for more details. |
||
19 | |||
20 | You should have received a copy of the GNU Lesser General Public License |
||
21 | along with TZipMaster. If not, see <http://www.gnu.org/licenses/>. |
||
22 | |||
23 | contact: problems@delphizip.org (include ZipMaster in the subject). |
||
24 | updates: http://www.delphizip.org |
||
25 | DelphiZip maillist subscribe at http://www.freelists.org/list/delphizip |
||
26 | |||
27 | modified 2010-06-26 |
||
28 | ---------------------------------------------------------------------------*) |
||
29 | |||
30 | {$INCLUDE '.\ZipVers19.inc'} |
||
31 | |||
32 | {$IFDEF VERD6up} |
||
33 | {$WARN UNIT_PLATFORM OFF} |
||
34 | {$WARN SYMBOL_PLATFORM OFF} |
||
35 | {$ENDIF} |
||
36 | |||
37 | interface |
||
38 | |||
39 | uses |
||
40 | SysUtils, Windows, Classes; |
||
41 | |||
42 | type |
||
43 | TPathSlashDirection = (psdExternal, psdInternal); |
||
44 | // DeleteOpts = (htdFinal, htdAllowUndo); |
||
45 | |||
46 | type |
||
47 | {$IFDEF UNICODE} |
||
48 | TZMRawBytes = RawByteString; |
||
49 | {$ELSE} |
||
50 | TZMRawBytes = AnsiString; |
||
51 | {$ENDIF} |
||
52 | |||
53 | |||
54 | const // QueryZip return bit values and errors |
||
55 | zqbStartEXE = 1; // is EXE file may be SFX |
||
56 | zqbStartLocal = 2; // normal zip file start |
||
57 | zqbStartSpan = 4; // first part of span |
||
58 | zqbStartCentral = 8; // continuing Central Header |
||
59 | zqbHasComment = 16; |
||
60 | // zqbGoodComment = 16; // comment length good (no junk at end) |
||
61 | zqbHasLocal = 32; // first Central entry points to local header |
||
62 | zqbHasCentral = 64; // Central entry where it should be |
||
63 | zqbHasEOC = 128; // End of Central entry |
||
64 | zqbHasLoc64 = 256; // EOC64 locator entry |
||
65 | zqbHasEOC64 = 512; // Zip64 EOC |
||
66 | zqbJunkAtEnd = 1024; // junk at end of zip |
||
67 | zqbIsDiskZero = 2048; // is disk 0 |
||
68 | |||
69 | zqFieldError = -5; // bad field value |
||
70 | zqFileError = -7; // file handling error |
||
71 | zqGeneralError = -9; // unspecified failure |
||
72 | |||
73 | |||
74 | function AbsErr(err: Integer): Integer; |
||
75 | function DelimitPath(const Path: String; Sep: Boolean): String; |
||
76 | |||
77 | function DirExists(const FName: String): Boolean; |
||
78 | |||
79 | function DiskAvailable(const path: String): Boolean; |
||
80 | |||
81 | function EraseFile(const FName: String; permanent: Boolean): Integer; |
||
82 | function ExtractNameOfFile(const FileName: String): String; |
||
83 | |||
84 | function ExeVersion(const FName: String; var MS, LS: DWORD): Boolean; |
||
85 | function ExeVers(const FName: String): Integer; |
||
86 | function VersStr(vers: Integer; Comma: Boolean = False): String; |
||
87 | |||
88 | function FileSeek64(Handle: Integer; const Offset: Int64; Origin: Integer): Int64; |
||
89 | |||
90 | // stable replacement for depreciated FileAge() |
||
91 | function File_Age(const FName: String): Cardinal; |
||
92 | |||
93 | procedure File_Close(var fh: Integer); |
||
94 | |||
95 | procedure File_Delete(const FName: String); |
||
96 | |||
97 | function File_Size(const FSpec: TFilename): Int64; |
||
98 | |||
99 | function ForceDirectory(const DirName: String): Boolean; |
||
100 | |||
101 | function GetVolumeLabel(const drive: String): String; |
||
102 | |||
103 | function Hi64(i: Int64): Cardinal; |
||
104 | |||
105 | function IsSameFile(const FName1, FName2: String): Boolean; |
||
106 | |||
107 | function IsWild(const FSpec: String): Boolean; |
||
108 | // returns position of first wild character or 0 |
||
109 | function HasWild(const FSpec: String): Integer; |
||
110 | function HasWildW(const FSpec: WideString): Integer; |
||
111 | |||
112 | // true we're running under XP or later. |
||
113 | function IsWinXP: Boolean; |
||
114 | function WinVersion: Integer; |
||
115 | |||
116 | function Lo64(i: Int64): Cardinal; |
||
117 | |||
118 | function PathConcat(const path, extra: String): String; |
||
119 | |||
120 | function QueryZip(const FName: String): Integer; |
||
121 | |||
122 | function SetSlash(const path: String; dir: TPathSlashDirection): String; |
||
123 | function SetSlashW(const path: WideString; dir: TPathSlashDirection): WideString; |
||
124 | |||
125 | function StrToOEM(const astr: String): String; |
||
126 | function OEMToStr(const astr: Ansistring): String; |
||
127 | //1 return True if contains chars (<#31 ?) >#126 |
||
128 | function StrHasExt(const astr: String): Boolean; overload; |
||
129 | {$IFDEF UNICODE} |
||
130 | function StrHasExt(const astr: AnsiString): Boolean; overload; |
||
131 | function StrHasExt(const astr: TZMRawBytes): Boolean; overload; |
||
132 | {$ENDIF} |
||
133 | function LastPos(const s: String; ch: Char; before: Integer = MAXINT): Integer; |
||
134 | function LastPosW(const s: WideString; wch: Widechar; before: Integer = MAXINT): Integer; |
||
135 | |||
136 | function CalcCRC32(const mem; len: Integer; init: DWORD): DWORD; |
||
137 | |||
138 | function OpenResStream(const ResName: String; const rtype: PChar): TResourceStream; |
||
139 | |||
140 | function IsFolder(const Name: String): Boolean; |
||
141 | {$IFDEF UNICODE} |
||
142 | overload; |
||
143 | function IsFolder(const name: TZMRawBytes): boolean; overload; |
||
144 | {$ENDIF} |
||
145 | |||
146 | function CanHash(const FSpec: String): Boolean; |
||
147 | |||
148 | // return true if filename is obviously invalid |
||
149 | function NameIsBad(const astr: String): Boolean; |
||
150 | |||
151 | // return exe size (if < 4G) |
||
152 | // 0 _ not exe |
||
153 | function ExeSize(const Name: String): Cardinal; overload; |
||
154 | function ExeSize(fileHandle: Integer): Cardinal; overload; |
||
155 | |||
156 | |||
157 | // check for SFX header or detached header |
||
158 | // return <0 error |
||
159 | const |
||
160 | cstNone = 0; // not found |
||
161 | cstExe = 1; // might be stub of unknown type |
||
162 | cstSFX17 = 17; // found 1.7 SFX headers |
||
163 | cstSFX19 = 19; // found 1.9 SFX headers |
||
164 | cstDetached = 2048; // is detached - if name specified ZipName will modified for it |
||
165 | |||
166 | function CheckSFXType(const fileHandle: Integer; var ZipName: String; |
||
167 | var size: Integer): Integer; overload; |
||
168 | function CheckSFXType(const Name: String; var ZipName: String; |
||
169 | var size: Integer): Integer; overload; |
||
170 | |||
171 | function FileDateToLocalDateTime(stamp: Integer): TDateTime; |
||
172 | |||
173 | // -------------------------- ------------ ------------------------- |
||
174 | implementation |
||
175 | |||
176 | uses ZMStructs19, ShellApi, Forms, ZMUTF819, ZMSFXInt19; |
||
177 | |||
178 | type |
||
179 | TInt64Rec = packed record |
||
180 | case Integer of |
||
181 | 0: (I: Int64); |
||
182 | 1: (Lo, Hi: Cardinal); |
||
183 | end; |
||
184 | |||
185 | const |
||
186 | CRC32Table: array[0..255] of DWORD = ( |
||
187 | $00000000, $77073096, $EE0E612C, $990951BA, |
||
188 | $076DC419, $706AF48F, $E963A535, $9E6495A3, |
||
189 | $0EDB8832, $79DCB8A4, $E0D5E91E, $97D2D988, |
||
190 | $09B64C2B, $7EB17CBD, $E7B82D07, $90BF1D91, |
||
191 | $1DB71064, $6AB020F2, $F3B97148, $84BE41DE, |
||
192 | $1ADAD47D, $6DDDE4EB, $F4D4B551, $83D385C7, |
||
193 | $136C9856, $646BA8C0, $FD62F97A, $8A65C9EC, |
||
194 | $14015C4F, $63066CD9, $FA0F3D63, $8D080DF5, |
||
195 | $3B6E20C8, $4C69105E, $D56041E4, $A2677172, |
||
196 | $3C03E4D1, $4B04D447, $D20D85FD, $A50AB56B, |
||
197 | $35B5A8FA, $42B2986C, $DBBBC9D6, $ACBCF940, |
||
198 | $32D86CE3, $45DF5C75, $DCD60DCF, $ABD13D59, |
||
199 | $26D930AC, $51DE003A, $C8D75180, $BFD06116, |
||
200 | $21B4F4B5, $56B3C423, $CFBA9599, $B8BDA50F, |
||
201 | $2802B89E, $5F058808, $C60CD9B2, $B10BE924, |
||
202 | $2F6F7C87, $58684C11, $C1611DAB, $B6662D3D, |
||
203 | |||
204 | $76DC4190, $01DB7106, $98D220BC, $EFD5102A, |
||
205 | $71B18589, $06B6B51F, $9FBFE4A5, $E8B8D433, |
||
206 | $7807C9A2, $0F00F934, $9609A88E, $E10E9818, |
||
207 | $7F6A0DBB, $086D3D2D, $91646C97, $E6635C01, |
||
208 | $6B6B51F4, $1C6C6162, $856530D8, $F262004E, |
||
209 | $6C0695ED, $1B01A57B, $8208F4C1, $F50FC457, |
||
210 | $65B0D9C6, $12B7E950, $8BBEB8EA, $FCB9887C, |
||
211 | $62DD1DDF, $15DA2D49, $8CD37CF3, $FBD44C65, |
||
212 | $4DB26158, $3AB551CE, $A3BC0074, $D4BB30E2, |
||
213 | $4ADFA541, $3DD895D7, $A4D1C46D, $D3D6F4FB, |
||
214 | $4369E96A, $346ED9FC, $AD678846, $DA60B8D0, |
||
215 | $44042D73, $33031DE5, $AA0A4C5F, $DD0D7CC9, |
||
216 | $5005713C, $270241AA, $BE0B1010, $C90C2086, |
||
217 | $5768B525, $206F85B3, $B966D409, $CE61E49F, |
||
218 | $5EDEF90E, $29D9C998, $B0D09822, $C7D7A8B4, |
||
219 | $59B33D17, $2EB40D81, $B7BD5C3B, $C0BA6CAD, |
||
220 | |||
221 | $EDB88320, $9ABFB3B6, $03B6E20C, $74B1D29A, |
||
222 | $EAD54739, $9DD277AF, $04DB2615, $73DC1683, |
||
223 | $E3630B12, $94643B84, $0D6D6A3E, $7A6A5AA8, |
||
224 | $E40ECF0B, $9309FF9D, $0A00AE27, $7D079EB1, |
||
225 | $F00F9344, $8708A3D2, $1E01F268, $6906C2FE, |
||
226 | $F762575D, $806567CB, $196C3671, $6E6B06E7, |
||
227 | $FED41B76, $89D32BE0, $10DA7A5A, $67DD4ACC, |
||
228 | $F9B9DF6F, $8EBEEFF9, $17B7BE43, $60B08ED5, |
||
229 | $D6D6A3E8, $A1D1937E, $38D8C2C4, $4FDFF252, |
||
230 | $D1BB67F1, $A6BC5767, $3FB506DD, $48B2364B, |
||
231 | $D80D2BDA, $AF0A1B4C, $36034AF6, $41047A60, |
||
232 | $DF60EFC3, $A867DF55, $316E8EEF, $4669BE79, |
||
233 | $CB61B38C, $BC66831A, $256FD2A0, $5268E236, |
||
234 | $CC0C7795, $BB0B4703, $220216B9, $5505262F, |
||
235 | $C5BA3BBE, $B2BD0B28, $2BB45A92, $5CB36A04, |
||
236 | $C2D7FFA7, $B5D0CF31, $2CD99E8B, $5BDEAE1D, |
||
237 | |||
238 | $9B64C2B0, $EC63F226, $756AA39C, $026D930A, |
||
239 | $9C0906A9, $EB0E363F, $72076785, $05005713, |
||
240 | $95BF4A82, $E2B87A14, $7BB12BAE, $0CB61B38, |
||
241 | $92D28E9B, $E5D5BE0D, $7CDCEFB7, $0BDBDF21, |
||
242 | $86D3D2D4, $F1D4E242, $68DDB3F8, $1FDA836E, |
||
243 | $81BE16CD, $F6B9265B, $6FB077E1, $18B74777, |
||
244 | $88085AE6, $FF0F6A70, $66063BCA, $11010B5C, |
||
245 | $8F659EFF, $F862AE69, $616BFFD3, $166CCF45, |
||
246 | $A00AE278, $D70DD2EE, $4E048354, $3903B3C2, |
||
247 | $A7672661, $D06016F7, $4969474D, $3E6E77DB, |
||
248 | $AED16A4A, $D9D65ADC, $40DF0B66, $37D83BF0, |
||
249 | $A9BCAE53, $DEBB9EC5, $47B2CF7F, $30B5FFE9, |
||
250 | $BDBDF21C, $CABAC28A, $53B39330, $24B4A3A6, |
||
251 | $BAD03605, $CDD70693, $54DE5729, $23D967BF, |
||
252 | $B3667A2E, $C4614AB8, $5D681B02, $2A6F2B94, |
||
253 | $B40BBE37, $C30C8EA1, $5A05DF1B, $2D02EF8D); |
||
254 | |||
255 | //-------------------------------------------------------- |
||
256 | function Lo64(i: Int64): Cardinal; |
||
257 | var |
||
258 | r: TInt64Rec; |
||
259 | begin |
||
260 | r.I := i; |
||
261 | Result := r.Lo; |
||
262 | end; |
||
263 | |||
264 | function Hi64(i: Int64): Cardinal; |
||
265 | var |
||
266 | r: TInt64Rec; |
||
267 | begin |
||
268 | r.I := i; |
||
269 | Result := r.Hi; |
||
270 | end; |
||
271 | |||
272 | //-------------------------------------------------------- |
||
273 | function AbsErr(err: Integer): Integer; |
||
274 | begin |
||
275 | if err < 0 then |
||
276 | Result := -err |
||
277 | else |
||
278 | Result := err; |
||
279 | end; |
||
280 | |||
281 | function DelimitPath(const Path: String; Sep: Boolean): String; |
||
282 | begin |
||
283 | Result := Path; |
||
284 | if Length(Path) = 0 then |
||
285 | begin |
||
286 | if Sep then |
||
287 | Result := PathDelim{'\'}; |
||
288 | exit; |
||
289 | end; |
||
290 | if (AnsiLastChar(Path)^ = PathDelim) <> Sep then |
||
291 | begin |
||
292 | if Sep then |
||
293 | Result := Path + PathDelim |
||
294 | else |
||
295 | Result := Copy(Path, 1, pred(Length(Path))); |
||
296 | end; |
||
297 | end; |
||
298 | |||
299 | (*? DirExists |
||
300 | 1.73 12 July 2003 return true empty string (current directory) |
||
301 | *) |
||
302 | function DirExists(const FName: String): Boolean; |
||
303 | var |
||
304 | Code: DWORD; |
||
305 | dir: String; |
||
306 | begin |
||
307 | Result := True; // current directory exists |
||
308 | dir := DelimitPath(FName, False); |
||
309 | if FName <> '' then |
||
310 | begin |
||
311 | Code := GetFileAttributes(PChar(dir{FName})); |
||
312 | Result := (Code <> MAX_UNSIGNED) and ((FILE_ATTRIBUTE_DIRECTORY and Code) <> 0); |
||
313 | end; |
||
314 | end; |
||
315 | |||
316 | function DiskAvailable(const path: String): Boolean; |
||
317 | var |
||
318 | drv: Integer; |
||
319 | em: Cardinal; |
||
320 | pth: String; |
||
321 | begin |
||
322 | Result := False; |
||
323 | pth := ExpandUNCFileName(path); |
||
324 | if (length(pth) > 1) and (pth[2] = DriveDelim) then |
||
325 | // if (length(pth) >1) and (pth[2] = ':') then |
||
326 | begin |
||
327 | drv := Ord(Uppercase(pth)[1]) - $40; |
||
328 | em := SetErrorMode(SEM_FAILCRITICALERRORS); |
||
329 | Result := DiskSize(drv) <> -1; |
||
330 | SetErrorMode(em); |
||
331 | end; |
||
332 | end; |
||
333 | |||
334 | (*? EraseFile |
||
335 | 1.77 moved from ZMaster |
||
336 | Delete a file and put it in the recyclebin on demand. |
||
337 | *) |
||
338 | function EraseFile(const FName: String; permanent: Boolean): Integer; |
||
339 | var |
||
340 | DelFileName: String; |
||
341 | SHF: TSHFileOpStruct; |
||
342 | begin |
||
343 | // If we do not have a full path then FOF_ALLOWUNDO does not work!? |
||
344 | DelFileName := FName; |
||
345 | if ExtractFilePath(FName) = '' then |
||
346 | DelFileName := GetCurrentDir() + PathDelim{'\'} + FName; |
||
347 | |||
348 | Result := -1; |
||
349 | // We need to be able to 'Delete' without getting an error |
||
350 | // if the file does not exists as in ReadSpan() can occur. |
||
351 | if not FileExists(DelFileName) then |
||
352 | Exit; |
||
353 | // with SHF do |
||
354 | // begin |
||
355 | SHF.Wnd := Application.Handle; |
||
356 | SHF.wFunc := FO_DELETE; |
||
357 | SHF.pFrom := PChar(DelFileName + #0); |
||
358 | SHF.pTo := nil; |
||
359 | SHF.fFlags := FOF_SILENT or FOF_NOCONFIRMATION; |
||
360 | if not permanent then |
||
361 | SHF.fFlags := SHF.fFlags or FOF_ALLOWUNDO; |
||
362 | // end; |
||
363 | Result := SHFileOperation(SHF); |
||
364 | end; |
||
365 | |||
366 | function ExeVersion(const FName: String; var MS, LS: DWORD): Boolean; |
||
367 | var |
||
368 | Dummy: DWORD; |
||
369 | VerInfo: Pointer; |
||
370 | VerInfoSize: DWORD; |
||
371 | VerValue: PVSFixedFileInfo; |
||
372 | VerValueSize: DWORD; |
||
373 | begin |
||
374 | Result := False; |
||
375 | if FileExists(FName) then |
||
376 | begin |
||
377 | VerInfoSize := GetFileVersionInfoSize(PChar(FName), Dummy); |
||
378 | GetMem(VerInfo, VerInfoSize); |
||
379 | try |
||
380 | if GetFileVersionInfo(PChar(FName), 0, VerInfoSize, VerInfo) then |
||
381 | begin |
||
382 | VerQueryValue(VerInfo, '\', Pointer(VerValue), VerValueSize); |
||
383 | MS := VerValue^.dwFileVersionMS; |
||
384 | LS := VerValue^.dwFileVersionLS; |
||
385 | Result := True; |
||
386 | end; |
||
387 | finally |
||
388 | FreeMem(VerInfo, VerInfoSize); |
||
389 | end; |
||
390 | end; |
||
391 | end; |
||
392 | |||
393 | // format M.N.RR.BBB |
||
394 | // return Version as used by DelphiZip |
||
395 | function ExeVers(const FName: String): Integer; |
||
396 | var |
||
397 | LS: DWORD; |
||
398 | MS: DWORD; |
||
399 | begin |
||
400 | Result := -1; |
||
401 | if ExeVersion(FName, MS, LS) then |
||
402 | begin |
||
403 | Result := (Integer(MS) shr 16) * 1000000; |
||
404 | Result := Result + (Integer(MS and $FFFF) * 100000); |
||
405 | Result := Result + ((Integer(LS) shr 16) * 10000); |
||
406 | Result := Result + Integer(LS and $FFFF) mod 1000; |
||
407 | end; |
||
408 | end; |
||
409 | |||
410 | function ExtractNameOfFile(const FileName: String): String; |
||
411 | var |
||
412 | I: Integer; |
||
413 | J: Integer; |
||
414 | begin |
||
415 | I := LastDelimiter(PathDelim + DriveDelim, FileName); |
||
416 | J := LastDelimiter('.', FileName); |
||
417 | if (J <= I) then |
||
418 | begin |
||
419 | J := MaxInt; |
||
420 | end; // no ext |
||
421 | Result := Copy(FileName, I + 1, J - (I + 1)); |
||
422 | end; |
||
423 | |||
424 | function VersStr(vers: Integer; Comma: Boolean = False): String; |
||
425 | const |
||
426 | fmt: array [Boolean] of String = |
||
427 | ('%d.%d.%d.%4.4d', '%d,%d,%d,%d'); |
||
428 | begin |
||
429 | Result := Format(fmt[Comma], [vers div 1000000, (vers mod 1000000) div |
||
430 | 100000, (vers mod 100000) div 10000, vers mod 1000]); |
||
431 | end; |
||
432 | |||
433 | function OpenResStream(const ResName: String; const rtype: PChar): TResourceStream; |
||
434 | var |
||
435 | hFindRes: Cardinal; |
||
436 | idNo: Integer; |
||
437 | inst: Integer; |
||
438 | rsn: PChar; |
||
439 | begin |
||
440 | Result := nil; |
||
441 | try |
||
442 | rsn := PChar(ResName); |
||
443 | // idno := 0; |
||
444 | inst := HInstance; |
||
445 | if (Length(ResName) > 1) and (ResName[1] = '#') then |
||
446 | begin |
||
447 | idNo := StrToInt(copy(ResName, 2, 25)); |
||
448 | rsn := PChar(idNo); |
||
449 | end; |
||
450 | hFindRes := FindResource(inst, rsn, rtype); |
||
451 | if (hFindRes = 0) and ModuleIsLib then |
||
452 | begin |
||
453 | inst := MainInstance; |
||
454 | hFindRes := FindResource(inst, rsn, rtype); |
||
455 | end; |
||
456 | if hFindRes <> 0 then |
||
457 | Result := TResourceStream.Create(inst, ResName, rtype); |
||
458 | except |
||
459 | Result := nil; |
||
460 | end; |
||
461 | end; |
||
462 | |||
463 | function File_Age(const FName: String): Cardinal; |
||
464 | var |
||
465 | FindData: TWin32FindData; |
||
466 | Handle: THandle; |
||
467 | LocalFileTime: TFileTime; |
||
468 | begin |
||
469 | Handle := FindFirstFile(PChar(FName), FindData); |
||
470 | if Handle <> INVALID_HANDLE_VALUE then |
||
471 | begin |
||
472 | FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime); |
||
473 | Windows.FindClose(Handle); |
||
474 | if FileTimeToDosDateTime(LocalFileTime, LongRec(Result).Hi, |
||
475 | LongRec(Result).Lo) then |
||
476 | Exit; |
||
477 | end; |
||
478 | Result := Cardinal(-1); |
||
479 | end; |
||
480 | |||
481 | procedure File_Close(var fh: Integer); |
||
482 | var |
||
483 | h: Integer; |
||
484 | begin |
||
485 | if fh <> Invalid_Handle then |
||
486 | begin |
||
487 | h := fh; |
||
488 | fh := Invalid_Handle; |
||
489 | FileClose(h); |
||
490 | end; |
||
491 | end; |
||
492 | |||
493 | procedure File_Delete(const FName: String); |
||
494 | begin |
||
495 | if FileExists(FName) then |
||
496 | SysUtils.DeleteFile(FName); |
||
497 | end; |
||
498 | |||
499 | function FileSeek64(Handle: Integer; const Offset: Int64; Origin: Integer): Int64; |
||
500 | {$IFDEF VERD6up} |
||
501 | begin |
||
502 | Result := FileSeek(Handle, Offset, Origin); |
||
503 | end; |
||
504 | {$ELSE} |
||
505 | //function FileSeek64(Handle: Integer; const Offset: Int64; Origin: Integer): Int64; |
||
506 | var |
||
507 | r: TInt64Rec; |
||
508 | begin |
||
509 | r.I := Offset; |
||
510 | r.Lo := SetFilePointer(Handle, Integer(r.Lo), @r.Hi, Origin); |
||
511 | if (r.Lo = Cardinal(-1)) and (GetLastError <> 0) then |
||
512 | r.I := -1; |
||
513 | Result := r.i; |
||
514 | end; |
||
515 | |||
516 | {$ENDIF} |
||
517 | |||
518 | function File_Size(const FSpec: TFilename): Int64; |
||
519 | var |
||
520 | sr: TSearchRec; |
||
521 | begin |
||
522 | Result := 0; |
||
523 | if SysUtils.FindFirst(FSpec, faAnyFile, sr) = 0 then |
||
524 | begin |
||
525 | Result := sr.Size; |
||
526 | SysUtils.FindClose(sr); |
||
527 | end; |
||
528 | end; |
||
529 | |||
530 | (*? ForceDirectory |
||
531 | 1.73 RP utilities |
||
532 | *) |
||
533 | function ForceDirectory(const DirName: String): Boolean; |
||
534 | var |
||
535 | sDir: String; |
||
536 | begin |
||
537 | Result := True; |
||
538 | if DirName <> '' then |
||
539 | begin |
||
540 | sDir := DelimitPath(DirName, False); |
||
541 | if DirExists(sDir) or (ExtractFilePath(sDir) = sDir) then |
||
542 | exit; // avoid 'c:\xyz:\' problem. |
||
543 | |||
544 | if ForceDirectory(ExtractFilePath(sDir)) then |
||
545 | Result := CreateDirectory(PChar(sDir), nil) |
||
546 | else |
||
547 | Result := False; |
||
548 | end; |
||
549 | end; |
||
550 | |||
551 | (*? HasWild |
||
552 | returns position of first wild character or 0 |
||
553 | *) |
||
554 | function HasWild(const FSpec: String): Integer; |
||
555 | var |
||
556 | c: Char; |
||
557 | i: Integer; |
||
558 | begin |
||
559 | Result := 0; |
||
560 | for i := 1 to Length(FSpec) do |
||
561 | begin |
||
562 | c := FSpec[i]; |
||
563 | if (c = WILD_MULTI) or (c = WILD_CHAR) then |
||
564 | begin |
||
565 | Result := i; |
||
566 | break; |
||
567 | end; |
||
568 | end; |
||
569 | end; |
||
570 | |||
571 | |||
572 | (*? HasWildW |
||
573 | returns position of first wild character or 0 |
||
574 | *) |
||
575 | function HasWildW(const FSpec: WideString): Integer; |
||
576 | var |
||
577 | c: Widechar; |
||
578 | i: Integer; |
||
579 | begin |
||
580 | Result := 0; |
||
581 | for i := 1 to Length(FSpec) do |
||
582 | begin |
||
583 | c := FSpec[i]; |
||
584 | if (c = WILD_MULTI) or (c = WILD_CHAR) then |
||
585 | begin |
||
586 | Result := i; |
||
587 | break; |
||
588 | end; |
||
589 | end; |
||
590 | end; |
||
591 | |||
592 | (*? IsWild |
||
593 | 1.73.4 |
||
594 | returns true if filespec contains wildcard(s) |
||
595 | *) |
||
596 | function IsWild(const FSpec: String): Boolean; |
||
597 | var |
||
598 | c: Char; |
||
599 | i: Integer; |
||
600 | len: Integer; |
||
601 | begin |
||
602 | Result := True; |
||
603 | len := Length(FSpec); |
||
604 | i := 1; |
||
605 | while i <= len do |
||
606 | begin |
||
607 | c := FSpec[i]; |
||
608 | if (c = WILD_MULTI) or (c = WILD_CHAR) then |
||
609 | exit; |
||
610 | Inc(i); |
||
611 | end; |
||
612 | Result := False; |
||
613 | end; |
||
614 | |||
615 | function CanHash(const FSpec: String): Boolean; |
||
616 | var |
||
617 | c: Char; |
||
618 | i: Integer; |
||
619 | len: Integer; |
||
620 | begin |
||
621 | Result := False; |
||
622 | len := Length(FSpec); |
||
623 | i := 1; |
||
624 | while i <= len do |
||
625 | begin |
||
626 | c := FSpec[i]; |
||
627 | if (c = WILD_MULTI) or (c = WILD_CHAR) or (c = SPEC_SEP) then |
||
628 | exit; |
||
629 | Inc(i); |
||
630 | end; |
||
631 | Result := True; |
||
632 | end; |
||
633 | |||
634 | // Returns a boolean indicating whether or not we're running under XP or later. |
||
635 | function IsWinXP: Boolean; |
||
636 | var |
||
637 | osv: TOSVERSIONINFO; |
||
638 | begin |
||
639 | osv.dwOSVersionInfoSize := sizeOf(OSVERSIONINFO); |
||
640 | GetVersionEx(osv); |
||
641 | // result := ( osv.dwPlatformId = VER_PLATFORM_WIN32_NT ); |
||
642 | Result := (osv.dwMajorVersion > 5) or ((osv.dwMajorVersion = 5) and |
||
643 | (osv.dwMinorVersion >= 1)); |
||
644 | end; |
||
645 | |||
646 | // Returns a boolean indicating whether or not we're running under XP or later. |
||
647 | function WinVersion: Integer; |
||
648 | var |
||
649 | osv: TOSVERSIONINFO; |
||
650 | begin |
||
651 | osv.dwOSVersionInfoSize := sizeOf(OSVERSIONINFO); |
||
652 | GetVersionEx(osv); |
||
653 | Result := (osv.dwMajorVersion * 100) + osv.dwMinorVersion; |
||
654 | end; |
||
655 | |||
656 | (*? SetSlash |
||
657 | 1.76 use enum TPathSlashDirection = (psdExternal, psdInternal) |
||
658 | 1.73 |
||
659 | forwardSlash = false = Windows normal backslash '\' |
||
660 | forwardSlash = true = forward slash '/' |
||
661 | *) |
||
662 | function SetSlash(const path: String; dir: TPathSlashDirection): String; |
||
663 | {$IFDEF Delphi7up} |
||
664 | begin |
||
665 | if dir = psdInternal then |
||
666 | Result := AnsiReplaceStr(path, PathDelim, PathDelimAlt) |
||
667 | else |
||
668 | Result := AnsiReplaceStr(path, PathDelimAlt, PathDelim); |
||
669 | end; |
||
670 | {$ELSE} |
||
671 | var |
||
672 | c, f, r: Char; |
||
673 | i, len: Integer; |
||
674 | begin |
||
675 | Result := path; |
||
676 | len := Length(path); |
||
677 | if dir = psdInternal then |
||
678 | begin |
||
679 | f := PathDelim{'\'}; |
||
680 | r := PathDelimAlt;//'/'; |
||
681 | end |
||
682 | else |
||
683 | begin |
||
684 | f := PathDelimAlt;//'/'; |
||
685 | r := PathDelim{'\'}; |
||
686 | end; |
||
687 | i := 1; |
||
688 | while i <= len do |
||
689 | begin |
||
690 | c := path[i]; |
||
691 | {$ifndef UNICODE} |
||
692 | if c in LeadBytes then |
||
693 | begin |
||
694 | Inc(i, 2); |
||
695 | continue; |
||
696 | end; |
||
697 | {$endif} |
||
698 | if c = f then |
||
699 | Result[i] := r; |
||
700 | Inc(i); |
||
701 | end; |
||
702 | end; |
||
703 | |||
704 | {$ENDIF} |
||
705 | |||
706 | function SetSlashW(const path: WideString; dir: TPathSlashDirection): WideString; |
||
707 | var |
||
708 | c: Widechar; |
||
709 | f: Widechar; |
||
710 | i: Integer; |
||
711 | len: Integer; |
||
712 | r: Widechar; |
||
713 | begin |
||
714 | Result := path; |
||
715 | len := Length(path); |
||
716 | if dir = psdInternal then |
||
717 | begin |
||
718 | f := PathDelim{'\'}; |
||
719 | r := PathDelimAlt;//'/'; |
||
720 | end |
||
721 | else |
||
722 | begin |
||
723 | f := PathDelimAlt;//'/'; |
||
724 | r := PathDelim{'\'}; |
||
725 | end; |
||
726 | i := 1; |
||
727 | while i <= len do |
||
728 | begin |
||
729 | c := path[i]; |
||
730 | if c = f then |
||
731 | Result[i] := r; |
||
732 | Inc(i); |
||
733 | end; |
||
734 | end; |
||
735 | |||
736 | //--------------------------------------------------------------------------- |
||
737 | // concat path |
||
738 | function PathConcat(const path, extra: String): String; |
||
739 | var |
||
740 | pathLen: Integer; |
||
741 | pathLst: Char; |
||
742 | begin |
||
743 | pathLen := Length(path); |
||
744 | Result := path; |
||
745 | if pathLen > 0 then |
||
746 | begin |
||
747 | // pathLst := path[pathLen]; |
||
748 | pathLst := AnsiLastChar(path)^; |
||
749 | if (pathLst <> DriveDelim{':'}) and (Length(extra) > 0) then |
||
750 | if (extra[1] = PathDelim{'\'}) = (pathLst = PathDelim{'\'}) then |
||
751 | if pathLst = PathDelim{'\'} then |
||
752 | Result := Copy(path, 1, pathLen - 1) // remove trailing |
||
753 | else |
||
754 | Result := path + PathDelim;//'\'; |
||
755 | end; |
||
756 | Result := Result + extra; |
||
757 | end; |
||
758 | |||
759 | |||
760 | //const // QueryZip return bit values and errors |
||
761 | // zqbStartEXE = 1; // is EXE file may be SFX |
||
762 | // zqbStartLocal = 2; // normal zip file start |
||
763 | // zqbStartSpan = 4; // first part of span |
||
764 | // zqbStartCentral = 8; // continuing Central Header |
||
765 | // zqbHasComment = 16; |
||
766 | //// zqbGoodComment = 16; // comment length good (no junk at end) |
||
767 | // zqbHasLocal = 32; // first Central entry points to local header |
||
768 | // zqbHasCentral = 64; // Central entry where it should be |
||
769 | // zqbHasEOC = 128; // End of Central entry |
||
770 | // zqbHasLoc64 = 256; // EOC64 locator entry |
||
771 | // zqbHasEOC64 = 512; // Zip64 EOC |
||
772 | // zqbJunkAtEnd = 1024; // junk at end of zip |
||
773 | // zqbIsDiskZero = 2048; // is disk 0 |
||
774 | |||
775 | // zqFieldError = -5; // bad field value |
||
776 | // zqFileError = -7; // file handling error |
||
777 | // zqGeneralError = -9; // unspecified failure |
||
778 | |||
779 | function QueryZip(const FName: String): Integer; |
||
780 | const |
||
781 | FileMask = (zqbStartEXE or zqbStartLocal or zqbStartSpan or |
||
782 | zqbStartCentral or zqbHasComment or zqbJunkAtEnd);//zqbGoodComment); |
||
783 | var |
||
784 | Buf: array of Byte; |
||
785 | BufPos: Integer; |
||
786 | CenDisk: Cardinal; |
||
787 | CenOfs: Int64; |
||
788 | DoCenDir: Boolean; |
||
789 | EOC: TZipEndOfCentral; |
||
790 | EOCLoc: TZip64EOCLocator; |
||
791 | EOCPossible: Boolean; |
||
792 | FileHandle: Integer; |
||
793 | File_Sze: Int64; |
||
794 | fn: String; |
||
795 | fs: Int64; |
||
796 | Need64: Boolean; |
||
797 | pEOC: PZipEndOfCentral; |
||
798 | pEOCLoc: PZip64EOCLocator; |
||
799 | Pos0: Integer; |
||
800 | ReadPos: Cardinal; |
||
801 | res: Integer; |
||
802 | Sig: Cardinal; |
||
803 | Size: Integer; |
||
804 | ThisDisk: Cardinal; |
||
805 | // tmp: Integer; |
||
806 | // tmp64: int64; |
||
807 | |||
808 | function NeedLoc64(const QEOC: TZipEndOfCentral): Boolean; |
||
809 | begin |
||
810 | Result := (QEOC.ThisDiskNo = MAX_WORD) or (QEOC.CentralDiskNo = MAX_WORD) or |
||
811 | (QEOC.CentralEntries = MAX_WORD) or (QEOC.TotalEntries = MAX_WORD) or |
||
812 | (QEOC.CentralSize = MAX_UNSIGNED) or (QEOC.CentralOffset = MAX_UNSIGNED); |
||
813 | end; |
||
814 | // check central entry and, if same disk, its local header signal |
||
815 | function CheckCen(fh: Integer; This_Disk: Cardinal; CenOf: Int64): Integer; |
||
816 | type |
||
817 | TXData_tag = packed record |
||
818 | tag: Word; |
||
819 | siz: Word; |
||
820 | end; |
||
821 | PXData_tag = ^TXData_tag; |
||
822 | |||
823 | var |
||
824 | ret: Integer; |
||
825 | CentralHead: TZipCentralHeader; |
||
826 | Sgn: Cardinal; |
||
827 | Ofs: Int64; |
||
828 | xbuf: array of Byte; |
||
829 | xlen, ver: Integer; |
||
830 | wtg, wsz: Word; |
||
831 | has64: Boolean; |
||
832 | p: PByte; |
||
833 | begin // verify start of central |
||
834 | ret := 0; |
||
835 | Result := zqFieldError; |
||
836 | if (FileSeek64(fh, CenOf, soFromBeginning) <> -1) and |
||
837 | (FileRead(fh, CentralHead, sizeof(CentralHead)) = sizeof(CentralHead)) and |
||
838 | (CentralHead.HeaderSig = CentralFileHeaderSig) then |
||
839 | begin |
||
840 | ret := zqbHasCentral; // has linked Central |
||
841 | if (CentralHead.DiskStart = This_Disk) then |
||
842 | begin |
||
843 | ver := CentralHead.VersionNeeded; |
||
844 | if (ver and VerMask) > ZIP64_VER then |
||
845 | exit; |
||
846 | Ofs := CentralHead.RelOffLocal; |
||
847 | if (Ofs = MAX_UNSIGNED) and ((ver and VerMask) >= ZIP64_VER) then |
||
848 | begin |
||
849 | if ver > 45 then |
||
850 | exit; // bad version |
||
851 | // have to read extra data |
||
852 | xlen := CentralHead.FileNameLen + CentralHead.ExtraLen; |
||
853 | SetLength(xbuf, xlen); // easier to read filename + extra |
||
854 | if FileRead(fh, xbuf, xlen) <> xlen then |
||
855 | exit; // error |
||
856 | // find Zip64 extra data |
||
857 | has64 := False; |
||
858 | xlen := CentralHead.ExtraLen; |
||
859 | p := @xbuf[CentralHead.FileNameLen]; |
||
860 | wsz := 0; // keep compiler happy |
||
861 | while xlen > sizeof(TXData_tag) do |
||
862 | begin |
||
863 | wtg := PXData_tag(p)^.tag; |
||
864 | wsz := PXData_tag(p)^.siz; |
||
865 | if wtg = Zip64_data_tag then |
||
866 | begin |
||
867 | has64 := xlen >= (wsz + sizeof(TXData_tag)); |
||
868 | break; |
||
869 | end; |
||
870 | Inc(p, wsz + sizeof(TXData_tag)); |
||
871 | end; |
||
872 | if (not has64) or (wsz > (xlen - sizeof(TXData_tag))) then |
||
873 | exit; // no data so rel ofs is bad |
||
874 | Inc(p, sizeof(TXData_tag)); // past header |
||
875 | // locate offset - values only exist if needed |
||
876 | if CentralHead.UncomprSize = MAX_UNSIGNED then |
||
877 | begin |
||
878 | if wsz < sizeof(Int64) then |
||
879 | exit; // bad |
||
880 | Inc(p, sizeof(Int64)); |
||
881 | Dec(wsz, sizeof(Int64)); |
||
882 | end; |
||
883 | if CentralHead.ComprSize = MAX_UNSIGNED then |
||
884 | begin |
||
885 | if wsz < sizeof(Int64) then |
||
886 | exit; // bad |
||
887 | Inc(p, sizeof(Int64)); |
||
888 | Dec(wsz, sizeof(Int64)); |
||
889 | end; |
||
890 | if wsz < sizeof(Int64) then |
||
891 | exit; // bad |
||
892 | Ofs := PInt64(p)^; |
||
893 | end; |
||
894 | if (FileSeek64(fh, Ofs{Int64(CentralHead.RelOffLocal)}, 0) <> -1) and |
||
895 | (FileRead(fh, Sgn, sizeof(Sgn)) = sizeof(Sgn)) and |
||
896 | (Sgn = LocalFileHeaderSig) then |
||
897 | ret := zqbHasCentral or zqbHasLocal; // linked local |
||
898 | end; |
||
899 | end; |
||
900 | Result := ret; |
||
901 | end; |
||
902 | |||
903 | begin |
||
904 | EOCPossible := False; |
||
905 | Result := zqFileError; |
||
906 | DoCenDir := True; // test central too |
||
907 | if (FName <> '') and (FName[1] = '|') then |
||
908 | begin |
||
909 | DoCenDir := False; |
||
910 | fn := copy(FName, 2, length(FName) - 1); |
||
911 | end |
||
912 | else |
||
913 | fn := FName; |
||
914 | fn := Trim(fn); |
||
915 | if fn = '' then |
||
916 | exit; |
||
917 | FileHandle := Invalid_Handle; |
||
918 | res := 0; |
||
919 | try |
||
920 | try |
||
921 | // Open the input archive, presumably the last disk. |
||
922 | FileHandle := FileOpen(fn, fmShareDenyWrite or fmOpenRead); |
||
923 | if FileHandle = Invalid_Handle then |
||
924 | exit; |
||
925 | Result := 0; // rest errors normally file too small |
||
926 | |||
927 | // first we check if the start of the file has an IMAGE_DOS_SIGNATURE |
||
928 | if (FileRead(FileHandle, Sig, sizeof(Cardinal)) <> sizeof(Cardinal)) then |
||
929 | exit; |
||
930 | if LongRec(Sig).Lo = IMAGE_DOS_SIGNATURE then |
||
931 | res := zqbStartEXE |
||
932 | else |
||
933 | if Sig = LocalFileHeaderSig then |
||
934 | res := zqbStartLocal |
||
935 | else |
||
936 | if Sig = CentralFileHeaderSig then |
||
937 | res := zqbStartCentral |
||
938 | // part of split Central Directory |
||
939 | else |
||
940 | if Sig = ExtLocalSig then |
||
941 | res := zqbStartSpan; // first part of span |
||
942 | |||
943 | // A test for a zip archive without a ZipComment. |
||
944 | fs := FileSeek64(FileHandle, -Int64(sizeof(EOC)), soFromEnd); |
||
945 | if fs = -1 then |
||
946 | exit; // not zip - too small |
||
947 | File_Sze := fs; |
||
948 | // try no comment |
||
949 | if (FileRead(FileHandle, EOC, sizeof(EOC)) = sizeof(EOC)) and |
||
950 | (EOC.HeaderSig = EndCentralDirSig) and (EOC.ZipCommentLen = 0) then |
||
951 | begin |
||
952 | EOCPossible := True; |
||
953 | res := res or zqbHasEOC;// or zqbGoodComment; // EOC |
||
954 | CenDisk := EOC.CentralDiskNo; |
||
955 | ThisDisk := EOC.ThisDiskNo; |
||
956 | CenOfs := EOC.CentralOffset; |
||
957 | Need64 := NeedLoc64(EOC); |
||
958 | if (CenDisk = 0) and (ThisDisk = 0) then |
||
959 | res := res or zqbIsDiskZero; |
||
960 | // check Zip64 EOC |
||
961 | if Need64 and (fs > sizeof(TZip64EOCLocator)) then |
||
962 | begin // check for locator |
||
963 | if (FileSeek64(FileHandle, fs - sizeof(TZip64EOCLocator), soFromBeginning) <> |
||
964 | -1) and (FileRead(FileHandle, EOCLoc, sizeof(TZip64EOCLocator)) = |
||
965 | sizeof(TZip64EOCLocator)) and (EOCLoc.LocSig = EOC64LocatorSig) then |
||
966 | begin // found possible locator |
||
967 | res := res or zqbHasLoc64; |
||
968 | CenDisk := 0; |
||
969 | ThisDisk := 1; |
||
970 | CenOfs := -1; |
||
971 | end; |
||
972 | end; |
||
973 | if DoCenDir and (CenDisk = ThisDisk) then |
||
974 | begin |
||
975 | res := res or CheckCen(FileHandle, ThisDisk, CenOfs); |
||
976 | exit; |
||
977 | end; |
||
978 | res := res and FileMask; // remove rest |
||
979 | end; |
||
980 | // try to locate EOC |
||
981 | Inc(File_Sze, sizeof(EOC)); |
||
982 | Size := MAX_WORD + sizeof(EOC) + sizeof(TZip64EOCLocator); |
||
983 | if Size > File_Sze then |
||
984 | Size := File_Sze; |
||
985 | SetLength(Buf, Size); |
||
986 | Pos0 := Size - (MAX_WORD + sizeof(TZipEndOfCentral)); |
||
987 | if Pos0 < 0 then |
||
988 | Pos0 := 0; // lowest buf position for eoc |
||
989 | ReadPos := File_Sze - Size; |
||
990 | if (FileSeek64(FileHandle, Int64(ReadPos), soFromBeginning) <> -1) and |
||
991 | (FileRead(FileHandle, Buf[0], Size) = Size) then |
||
992 | begin |
||
993 | // Finally try to find the EOC record within the last 65K... |
||
994 | BufPos := Size - (sizeof(EOC)); |
||
995 | pEOC := PZipEndOfCentral(@Buf[Size - sizeof(EOC)]); |
||
996 | // reverse search |
||
997 | while BufPos > Pos0 do // reverse search |
||
998 | begin |
||
999 | Dec(BufPos); |
||
1000 | Dec(PAnsiChar(pEOC)); |
||
1001 | if pEOC^.HeaderSig = EndCentralDirSig then |
||
1002 | begin // possible EOC found |
||
1003 | res := res or zqbHasEOC; // EOC |
||
1004 | // check correct length comment |
||
1005 | if (BufPos + sizeof(EOC) + pEOC^.ZipCommentLen) <= Size then |
||
1006 | res := res or zqbHasComment; // good comment length |
||
1007 | if (BufPos + sizeof(EOC) + pEOC^.ZipCommentLen) <> Size then |
||
1008 | res := res or zqbJunkAtEnd; // has junk |
||
1009 | CenDisk := pEOC^.CentralDiskNo; |
||
1010 | ThisDisk := pEOC^.ThisDiskNo; |
||
1011 | if (CenDisk = 0) and (ThisDisk = 0) then |
||
1012 | res := res or zqbIsDiskZero; |
||
1013 | CenOfs := pEOC^.CentralOffset; |
||
1014 | Need64 := NeedLoc64(pEOC^); |
||
1015 | // check Zip64 EOC |
||
1016 | if Need64 and ((BufPos - sizeof(TZip64EOCLocator)) >= 0) then |
||
1017 | begin // check for locator |
||
1018 | pEOCLoc := PZip64EOCLocator(@Buf[BufPos - sizeof(TZip64EOCLocator)]); |
||
1019 | if pEOCLoc^.LocSig = EOC64LocatorSig then |
||
1020 | begin // found possible locator |
||
1021 | res := res or zqbHasLoc64; |
||
1022 | CenDisk := 0; |
||
1023 | ThisDisk := 1; |
||
1024 | CenOfs := -1; |
||
1025 | end; |
||
1026 | end; |
||
1027 | if DoCenDir and (CenDisk = ThisDisk) then |
||
1028 | begin // verify start of central |
||
1029 | res := res or CheckCen(FileHandle, ThisDisk, CenOfs); |
||
1030 | break; |
||
1031 | end; |
||
1032 | res := res and FileMask; // remove rest |
||
1033 | break; |
||
1034 | end; |
||
1035 | end; // while |
||
1036 | end; |
||
1037 | if EOCPossible then |
||
1038 | res := res or zqbHasEOC; |
||
1039 | except |
||
1040 | Result := zqGeneralError; |
||
1041 | end; |
||
1042 | finally |
||
1043 | File_Close(FileHandle); |
||
1044 | if Result = 0 then |
||
1045 | Result := res; |
||
1046 | end; |
||
1047 | end; |
||
1048 | //? QueryZip |
||
1049 | |||
1050 | function GetVolumeLabel(const drive: String): String; |
||
1051 | var |
||
1052 | Bits: set of 0..25; |
||
1053 | DriveLetter: Char; |
||
1054 | drv: String; |
||
1055 | NamLen: Cardinal; |
||
1056 | Num: Integer; |
||
1057 | OldErrMode: DWord; |
||
1058 | SysFlags: DWord; |
||
1059 | SysLen: DWord; |
||
1060 | VolNameAry: array[0..MAX_BYTE] of Char; |
||
1061 | begin |
||
1062 | Result := ''; |
||
1063 | NamLen := MAX_BYTE; |
||
1064 | SysLen := MAX_BYTE;; |
||
1065 | VolNameAry[0] := #0; |
||
1066 | drv := UpperCase(ExpandFileName(drive)); |
||
1067 | DriveLetter := drv[1]; |
||
1068 | if DriveLetter <> PathDelim{'\'} then // Only for local drives |
||
1069 | begin |
||
1070 | if (DriveLetter < 'A') or (DriveLetter > 'Z') then |
||
1071 | exit; |
||
1072 | Integer(Bits) := GetLogicalDrives(); |
||
1073 | Num := Ord(DriveLetter) - Ord('A'); |
||
1074 | if not (Num in Bits) then |
||
1075 | exit; |
||
1076 | end; |
||
1077 | OldErrMode := SetErrorMode(SEM_FAILCRITICALERRORS); |
||
1078 | // Turn off critical errors: |
||
1079 | if GetVolumeInformation(PChar(drv), VolNameAry, NamLen, nil, SysLen, |
||
1080 | SysFlags, nil, 0) then |
||
1081 | Result := VolNameAry; |
||
1082 | SetErrorMode(OldErrMode); |
||
1083 | // Restore critical errors: |
||
1084 | end; |
||
1085 | |||
1086 | function IsSameFile(const FName1, FName2: String): Boolean; |
||
1087 | var |
||
1088 | ff1: Boolean; |
||
1089 | ff2: Boolean; |
||
1090 | sr1: TSearchRec; |
||
1091 | sr2: TSearchRec; |
||
1092 | begin |
||
1093 | if CompareText(ExpandFileName(FName1), ExpandFileName(FName2)) = 0 then |
||
1094 | begin |
||
1095 | Result := True; |
||
1096 | exit; |
||
1097 | end; |
||
1098 | Result := False; |
||
1099 | // in windows no alias so names must match |
||
1100 | if CompareText(ExtractFileName(FName1), ExtractFileName(FName2)) = 0 then |
||
1101 | begin |
||
1102 | ff1 := FindFirst(FName1, faAnyFile, sr1) = 0; |
||
1103 | ff2 := FindFirst(FName2, faAnyFile, sr2) = 0; |
||
1104 | if (ff1 = ff2) and not ff1 then |
||
1105 | exit;// neither found assume different |
||
1106 | // Result := CompareText(FName1, FName2) = 0; |
||
1107 | { $ WARN SYMBOL_PLATFORM OFF} |
||
1108 | if ff1 = ff2 then |
||
1109 | Result := CompareMem(@sr1.FindData, @sr2.FindData, 2 + (4 * 4));// both exist |
||
1110 | if ff1 then |
||
1111 | SysUtils.FindClose(sr1); |
||
1112 | if ff2 then |
||
1113 | SysUtils.FindClose(sr2); |
||
1114 | end; |
||
1115 | end; |
||
1116 | |||
1117 | function OEMToStr(const astr: Ansistring): String; |
||
1118 | var |
||
1119 | buf: String; |
||
1120 | begin |
||
1121 | SetLength(buf, Length(astr) + 3); // allow worst case |
||
1122 | OemToChar(PAnsiChar(astr), PChar(buf)); |
||
1123 | Result := PChar(buf); |
||
1124 | end; |
||
1125 | |||
1126 | function StrToOEM(const astr: String): String; |
||
1127 | var |
||
1128 | buf: Ansistring; |
||
1129 | begin |
||
1130 | SetLength(buf, Length(astr) + 3); // allow worst case |
||
1131 | CharToOem(PChar(astr), PAnsiChar(buf)); |
||
1132 | buf := PAnsiChar(buf); // remove trailing nul |
||
1133 | Result := String(buf); |
||
1134 | end; |
||
1135 | |||
1136 | { |
||
1137 | return true if contains chars (<#31 ?) >#126 |
||
1138 | } |
||
1139 | function StrHasExt(const astr: String): Boolean; |
||
1140 | var |
||
1141 | i: Integer; |
||
1142 | begin |
||
1143 | Result := False; |
||
1144 | for i := 1 to Length(astr) do |
||
1145 | if (astr[i] > #126) or (astr[i] < #31) then |
||
1146 | begin |
||
1147 | Result := True; |
||
1148 | break; |
||
1149 | end; |
||
1150 | end; |
||
1151 | |||
1152 | {$IFDEF UNICODE} |
||
1153 | function StrHasExt(const astr: AnsiString): Boolean; |
||
1154 | var |
||
1155 | i: integer; |
||
1156 | begin |
||
1157 | Result := false; |
||
1158 | for i := 1 to Length(astr) do |
||
1159 | if (astr[i] > #126) or (astr[i] < #31) then |
||
1160 | begin |
||
1161 | Result := True; |
||
1162 | break; |
||
1163 | end; |
||
1164 | end; |
||
1165 | |||
1166 | function StrHasExt(const astr: TZMRawBytes): Boolean; |
||
1167 | var |
||
1168 | i: integer; |
||
1169 | begin |
||
1170 | Result := false; |
||
1171 | for i := 1 to Length(astr) do |
||
1172 | if (astr[i] > #126) or (astr[i] < #31) then |
||
1173 | begin |
||
1174 | Result := True; |
||
1175 | break; |
||
1176 | end; |
||
1177 | end; |
||
1178 | {$ENDIF} |
||
1179 | |||
1180 | function CalcCRC32(const mem; len: Integer; init: DWORD): DWORD; |
||
1181 | var |
||
1182 | i: Integer; |
||
1183 | p: pByte; |
||
1184 | begin |
||
1185 | p := @mem; |
||
1186 | Result := init; |
||
1187 | if (p <> nil) and (len > 0) then |
||
1188 | begin |
||
1189 | Result := Result xor $FFFFFFFF; |
||
1190 | for i := 1 to len do |
||
1191 | begin |
||
1192 | Result := (Result shr 8) xor CRC32Table[(p^ xor Byte(Result))]; |
||
1193 | Inc(p); |
||
1194 | end; |
||
1195 | Result := Result xor $FFFFFFFF; |
||
1196 | end; |
||
1197 | end; |
||
1198 | |||
1199 | function LastPos(const s: String; ch: Char; before: Integer = MAXINT): Integer; |
||
1200 | var |
||
1201 | i: Integer; |
||
1202 | begin |
||
1203 | Result := 0; // not found |
||
1204 | for i := 1 to Length(s) do |
||
1205 | begin |
||
1206 | if i >= before then |
||
1207 | break; |
||
1208 | if s[i] = ch then |
||
1209 | Result := i; |
||
1210 | end; |
||
1211 | end; |
||
1212 | |||
1213 | function LastPosW(const s: WideString; wch: Widechar; before: Integer = MAXINT): Integer; |
||
1214 | var |
||
1215 | i: Integer; |
||
1216 | begin |
||
1217 | Result := 0; // not found |
||
1218 | for i := 1 to Length(s) do |
||
1219 | begin |
||
1220 | if i >= before then |
||
1221 | break; |
||
1222 | if s[i] = wch then |
||
1223 | Result := i; |
||
1224 | end; |
||
1225 | end; |
||
1226 | |||
1227 | function IsFolder(const Name: String): Boolean; |
||
1228 | var |
||
1229 | ch: Char; |
||
1230 | begin |
||
1231 | Result := False; |
||
1232 | if Name <> '' then |
||
1233 | begin |
||
1234 | ch := Name[Length(Name)]; |
||
1235 | Result := (ch = PathDelim) or (ch = PathDelimAlt); |
||
1236 | end; |
||
1237 | end; |
||
1238 | |||
1239 | |||
1240 | {$IFDEF UNICODE} |
||
1241 | function IsFolder(const name: TZMRawBytes): boolean; |
||
1242 | var |
||
1243 | ch: AnsiChar; |
||
1244 | begin |
||
1245 | Result := False; |
||
1246 | if name <> '' then |
||
1247 | begin |
||
1248 | ch := name[Length(name)]; |
||
1249 | Result := (ch = PathDelim) or (ch = PathDelimAlt); |
||
1250 | end; |
||
1251 | end; |
||
1252 | {$ENDIF} |
||
1253 | |||
1254 | // return true if filename is obviously invalid |
||
1255 | function NameIsBad(const astr: String): Boolean; |
||
1256 | var |
||
1257 | i: Integer; |
||
1258 | begin |
||
1259 | Result := (astr = '') or (astr[1] = ' ') or (astr[1] = '\') or |
||
1260 | (Length(astr) > MAX_PATH); |
||
1261 | if not Result then |
||
1262 | for i := 1 to Length(astr) do |
||
1263 | {$IFDEF UNICODE} |
||
1264 | if CharInSet(astr[i], [#0..#31, ':', '<', '>', '|', '*', '?']) then |
||
1265 | {$ELSE} |
||
1266 | if astr[i] in [#0..#31, ':', '<', '>', '|', '*', '?'] then |
||
1267 | {$ENDIF} |
||
1268 | begin |
||
1269 | Result := True; |
||
1270 | break; |
||
1271 | end; |
||
1272 | if not Result then |
||
1273 | Result := (AnsiPos('..', astr) > 0) or (AnsiPos('\ ', astr) > 0) or |
||
1274 | (AnsiPos(' \', astr) > 0); |
||
1275 | end; |
||
1276 | |||
1277 | // return exe size (if < 4G) |
||
1278 | // 0 _ not exe |
||
1279 | function ExeSize(fileHandle: Integer): Cardinal; |
||
1280 | var |
||
1281 | bad: Boolean; |
||
1282 | did: Integer; |
||
1283 | sig: DWORD; |
||
1284 | dosHeader: TImageDOSHeader; |
||
1285 | fileHeader: TImageFileHeader; |
||
1286 | sectionHeader: TImageSectionHeader; |
||
1287 | i, NumSections: Integer; |
||
1288 | sectionEnd: Cardinal; |
||
1289 | const |
||
1290 | IMAGE_PE_SIGNATURE = $00004550; |
||
1291 | IMAGE_DOS_SIGNATURE = $5A4D; |
||
1292 | IMAGE_FILE_MACHINE_I386 = $14C; |
||
1293 | begin |
||
1294 | Result := 0; |
||
1295 | bad := True; |
||
1296 | if fileHandle <> -1 then |
||
1297 | begin |
||
1298 | try |
||
1299 | FileSeek(fileHandle, 0, soFromBeginning); |
||
1300 | while True do |
||
1301 | begin |
||
1302 | did := FileRead(fileHandle, dosHeader, sizeof(TImageDOSHeader)); |
||
1303 | if (did <> sizeof(TImageDOSHeader)) or |
||
1304 | (dosHeader.e_magic <> IMAGE_DOS_SIGNATURE) then |
||
1305 | break; |
||
1306 | if FileSeek(fileHandle, dosHeader._lfanew, 0) < 0 then |
||
1307 | break; |
||
1308 | did := FileRead(fileHandle, sig, sizeof(DWORD)); |
||
1309 | if (did <> sizeof(DWORD)) or (sig <> IMAGE_PE_SIGNATURE) then |
||
1310 | break; |
||
1311 | did := FileRead(fileHandle, fileHeader, sizeof(TImageFileHeader)); |
||
1312 | if (did <> sizeof(TImageFileHeader)) or |
||
1313 | (fileHeader.Machine <> IMAGE_FILE_MACHINE_I386) then |
||
1314 | break; |
||
1315 | NumSections := fileHeader.NumberOfSections; |
||
1316 | if FileSeek(fileHandle, sizeof(TImageOptionalHeader), 1) < 0 then |
||
1317 | break; |
||
1318 | bad := False; |
||
1319 | for i := 1 to NumSections do |
||
1320 | begin |
||
1321 | did := FileRead(fileHandle, sectionHeader, sizeof(TImageSectionHeader)); |
||
1322 | if (did <> sizeof(TImageSectionHeader)) then |
||
1323 | begin |
||
1324 | bad := True; |
||
1325 | break; |
||
1326 | end; |
||
1327 | sectionEnd := sectionHeader.PointerToRawData + sectionHeader.SizeOfRawData; |
||
1328 | if sectionEnd > Result then |
||
1329 | Result := sectionEnd; |
||
1330 | end; |
||
1331 | end; |
||
1332 | except |
||
1333 | bad := True; |
||
1334 | end; |
||
1335 | end; |
||
1336 | if bad then |
||
1337 | Result := 0; |
||
1338 | end; |
||
1339 | |||
1340 | function ExeSize(const Name: String): Cardinal; |
||
1341 | var |
||
1342 | fh: Integer; |
||
1343 | begin |
||
1344 | Result := 0; |
||
1345 | fh := FileOpen(Name, fmOpenRead); |
||
1346 | if fh <> -1 then |
||
1347 | begin |
||
1348 | Result := ExeSize(fh); |
||
1349 | File_Close(fh); |
||
1350 | end; |
||
1351 | end; |
||
1352 | |||
1353 | // return <0 error |
||
1354 | //const |
||
1355 | // cstNone = 0; // not found |
||
1356 | // cstExe = 1; // might be stub of unknown type |
||
1357 | // cstSFX17 = 2; // found 1.7 SFX headers |
||
1358 | // cstSFX19 = 4; // found 2.0 SFX headers |
||
1359 | // cstDetached = 64; // is detached |
||
1360 | // -7 = Open, read or seek error |
||
1361 | // -8 = memory error |
||
1362 | // -9 = exception error |
||
1363 | // -10 = all other exceptions |
||
1364 | |||
1365 | // check for SFX header or detached header |
||
1366 | function CheckSFXType(const fileHandle: Integer; var ZipName: String; |
||
1367 | var size: Integer): Integer; |
||
1368 | type |
||
1369 | T_header = packed record |
||
1370 | Sig: DWORD; |
||
1371 | Size: Word; |
||
1372 | X: Word; |
||
1373 | end; |
||
1374 | var |
||
1375 | nsize: Integer; |
||
1376 | hed: T_header; |
||
1377 | SFXHeader_end: TSFXFileEndOfHeader_17; |
||
1378 | Detached: TSFXDetachedHeader_17; |
||
1379 | tmp: Ansistring; |
||
1380 | begin |
||
1381 | Result := 0; // default none |
||
1382 | try |
||
1383 | size := ExeSize(fileHandle); |
||
1384 | if size > 0 then |
||
1385 | begin |
||
1386 | ZipName := ExtractNameOfFile(ZipName) + '.zip'; // use default |
||
1387 | while Result = 0 do // HOTFIX-MARX-A |
||
1388 | begin |
||
1389 | Result := -7; // error - maybe read error? |
||
1390 | if FileSeek(fileHandle, size, soFromBeginning) <> size then |
||
1391 | Break; |
||
1392 | // at end of stub - read file header |
||
1393 | if FileRead(fileHandle, hed, sizeof(T_header)) <> sizeof(T_header) then |
||
1394 | break; |
||
1395 | // valid? |
||
1396 | case hed.Sig of |
||
1397 | SFX_HEADER_SIG: |
||
1398 | begin |
||
1399 | // it is new header |
||
1400 | size := size + sizeof(T_header); |
||
1401 | // skip file header |
||
1402 | nsize := Hed.Size - SizeOf(T_header); |
||
1403 | if FileSeek(fileHandle, nsize, soFromCurrent) < 0 then |
||
1404 | break; // error |
||
1405 | // at end of stub - read file header |
||
1406 | if FileRead(fileHandle, hed, sizeof(T_header)) <> sizeof(T_header) then |
||
1407 | break; // invalid |
||
1408 | size := size + nsize; |
||
1409 | if hed.Sig = CentralFileHeaderSig then |
||
1410 | Result := cstSFX19 or cstDetached // found new detached |
||
1411 | else |
||
1412 | if hed.Sig = LocalFileHeaderSig then |
||
1413 | Result := cstSFX19; // found new |
||
1414 | Break; |
||
1415 | end; |
||
1416 | SFX_HEADER_SIG_17: |
||
1417 | begin |
||
1418 | // is old header |
||
1419 | size := size + sizeof(T_header); |
||
1420 | // skip file header |
||
1421 | nsize := Hed.Size - SizeOf(T_header); |
||
1422 | if FileSeek(fileHandle, nsize, soFromCurrent) < 0 then |
||
1423 | break; // error |
||
1424 | if FileRead(fileHandle, SFXHeader_end, sizeof(SFXHeader_end)) <> |
||
1425 | sizeof(SFXHeader_end) then |
||
1426 | break; // invalid |
||
1427 | if (SFXHeader_end.Signature <> SFX_HEADER_END_SIG_17) then |
||
1428 | break; // invalid |
||
1429 | // ignore header size check |
||
1430 | size := size + nsize + sizeof(SFXHeader_end); |
||
1431 | // at end of file header - check for detached header |
||
1432 | if FileRead(fileHandle, detached, sizeof(TSFXDetachedHeader_17)) <> |
||
1433 | sizeof(TSFXDetachedHeader_17) then |
||
1434 | break; // not detached |
||
1435 | if detached.Signature = SFX_DETACHED_HEADER_SIG_17 then |
||
1436 | begin |
||
1437 | size := size + sizeof(TSFXDetachedHeader_17); |
||
1438 | if Detached.NameLen > 0 then |
||
1439 | begin |
||
1440 | SetLength(tmp, Detached.NameLen); |
||
1441 | if FileRead(fileHandle, PAnsiChar(tmp)^, Detached.NameLen) <> |
||
1442 | Integer(Detached.NameLen) then |
||
1443 | break; // invalid |
||
1444 | ZipName := String(tmp) + ExtractFileExt(ZipName); |
||
1445 | size := size + Integer(Detached.NameLen); |
||
1446 | end; |
||
1447 | if Detached.ExtLen > 0 then |
||
1448 | begin |
||
1449 | SetLength(tmp, Detached.ExtLen); |
||
1450 | if FileRead(fileHandle, PAnsiChar(tmp)^, Detached.ExtLen) <> |
||
1451 | Integer(Detached.ExtLen) then |
||
1452 | break; // invalid |
||
1453 | size := size + Integer(Detached.ExtLen); |
||
1454 | ZipName := ExtractNameOfFile(ZipName) + '.' + string(tmp); |
||
1455 | end; |
||
1456 | // at end of file header - check for detached header end |
||
1457 | if (FileRead(fileHandle, detached, sizeof(TSFXDetachedHeader_17)) <> |
||
1458 | sizeof(TSFXDetachedHeader_17)) or |
||
1459 | (detached.Signature <> SFX_DETACHED_HEADER_END_SIG_17) then |
||
1460 | break; // invalid |
||
1461 | size := size + sizeof(TSFXDetachedHeader_17); |
||
1462 | if FileRead(fileHandle, hed, sizeof(DWORD)) <> sizeof(DWORD) then |
||
1463 | break; // invalid |
||
1464 | if hed.Sig = CentralFileHeaderSig then |
||
1465 | Result := cstSFX17 or cstDetached; // found old detached |
||
1466 | end; |
||
1467 | if detached.Signature = LocalFileHeaderSig then |
||
1468 | Result := cstSFX17; // found old |
||
1469 | Break; |
||
1470 | end; |
||
1471 | else |
||
1472 | Result := cstExe; // possibly stub of different loader |
||
1473 | end; |
||
1474 | end; |
||
1475 | end; |
||
1476 | except |
||
1477 | Result := -10; |
||
1478 | end; |
||
1479 | end; |
||
1480 | |||
1481 | function CheckSFXType(const Name: String; var ZipName: String; |
||
1482 | var size: Integer): Integer; |
||
1483 | var |
||
1484 | fh: Integer; |
||
1485 | begin |
||
1486 | Result := 0; |
||
1487 | if AnsiCompareText(ExtractFileExt(Name), '.exe') = 0 then |
||
1488 | begin |
||
1489 | fh := FileOpen(Name, fmOpenRead); |
||
1490 | if fh <> -1 then |
||
1491 | begin |
||
1492 | ZipName := Name; |
||
1493 | Result := CheckSFXType(fh, ZipName, size); |
||
1494 | File_Close(fh); |
||
1495 | end; |
||
1496 | end; |
||
1497 | end; |
||
1498 | |||
1499 | function FileDateToLocalDateTime(stamp: Integer): TDateTime; |
||
1500 | var |
||
1501 | LocTime, FTime: TFileTime; |
||
1502 | SysTime: TSystemTime; |
||
1503 | begin |
||
1504 | Result := 0; |
||
1505 | if DosDateTimeToFileTime(LongRec(stamp).Hi, LongRec(stamp).Lo, LocTime) and |
||
1506 | LocalFileTimeToFileTime(LocTime, FTime) and |
||
1507 | FileTimeToSystemTime(FTime, SysTime) then |
||
1508 | Result := SystemTimeToDateTime(SysTime); |
||
1509 | end; |
||
1510 | |||
1511 | end. |
||
1512 |