Setup.ScriptFunc.HelperFunc.pas 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. unit Setup.ScriptFunc.HelperFunc;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Helper types and functions for the script support functions (run time - used by Setup)
  8. }
  9. interface
  10. uses
  11. Windows, Diagnostics,
  12. uPSRuntime, MD5, SHA1,
  13. Shared.CommonFunc, Shared.FileClass, Setup.MainForm, Setup.WizardForm,
  14. Setup.UninstallProgressForm, Setup.Install, Compression.SevenZipDecoder;
  15. type
  16. { Must keep this in synch with Compiler.ScriptFunc.pas }
  17. TOnLog = procedure(const S: String; const Error, FirstLine: Boolean) of object;
  18. { Must keep this in synch with Compiler.ScriptFunc.pas }
  19. TFindRec = record
  20. Name: String;
  21. Attributes: LongWord;
  22. SizeHigh: LongWord;
  23. SizeLow: LongWord;
  24. CreationTime: TFileTime;
  25. LastAccessTime: TFileTime;
  26. LastWriteTime: TFileTime;
  27. AlternateName: String;
  28. FindHandle: THandle;
  29. end;
  30. { Must keep this in synch with Compiler.ScriptFunc.pas }
  31. TWindowsVersion = packed record
  32. Major: Cardinal;
  33. Minor: Cardinal;
  34. Build: Cardinal;
  35. ServicePackMajor: Cardinal;
  36. ServicePackMinor: Cardinal;
  37. NTPlatform: Boolean;
  38. ProductType: Byte;
  39. SuiteMask: Word;
  40. end;
  41. { Makes sure script isn't called crazy often because that would slow the download significantly.
  42. Only reports:
  43. -At start or finish
  44. -If at least 50 ms passed since last report }
  45. TProgressThrottler = class
  46. private
  47. FOnDownloadProgress: TOnDownloadProgress;
  48. FOnExtractionProgress: TOnExtractionProgress;
  49. FStopWatch: TStopWatch;
  50. FLastOkProgress: Int64;
  51. function ThrottleOk(const Progress, ProgressMax: Int64): Boolean;
  52. public
  53. constructor Create(const OnDownloadProgress: TOnDownloadProgress); overload;
  54. constructor Create(const OnExtractionProgress: TOnExtractionProgress); overload;
  55. procedure Reset;
  56. function OnDownloadProgress(const Url, BaseName: string; const Progress, ProgressMax: Int64): Boolean;
  57. function OnExtractionProgress(const ArchiveName, FileName: string; const Progress, ProgressMax: Int64): Boolean;
  58. end;
  59. var
  60. ScaleBaseUnitX, ScaleBaseUnitY: Integer;
  61. procedure NoUninstallFuncError(const C: AnsiString); overload;
  62. procedure OnlyUninstallFuncError(const C: AnsiString); overload;
  63. function GetWizardForm: TWizardForm;
  64. function GetUninstallProgressForm: TUninstallProgressForm;
  65. function GetMsgBoxCaption: String;
  66. procedure InitializeScaleBaseUnits;
  67. function IsProtectedSrcExe(const Filename: String): Boolean;
  68. function FindFirstHelper(const FileName: String; var FindRec: TFindRec): Boolean;
  69. function FindNextHelper(var FindRec: TFindRec): Boolean;
  70. procedure FindCloseHelper(var FindRec: TFindRec);
  71. function FmtMessageHelper(const S: String; const Args: array of String): String;
  72. procedure GetWindowsVersionExHelper(var Version: TWindowsVersion);
  73. procedure CrackCodeRootKey(CodeRootKey: HKEY; var RegView: TRegView;
  74. var RootKey: HKEY);
  75. function GetSubkeyOrValueNames(const RegView: TRegView; const RootKey: HKEY;
  76. const SubKeyName: String; const Stack: TPSStack; const ItemNo: Longint; const Subkey: Boolean): Boolean;
  77. function GetMD5OfFile(const DisableFsRedir: Boolean; const Filename: String): TMD5Digest;
  78. function GetSHA1OfFile(const DisableFsRedir: Boolean; const Filename: String): TSHA1Digest;
  79. function GetMD5OfAnsiString(const S: AnsiString): TMD5Digest;
  80. function GetMD5OfUnicodeString(const S: UnicodeString): TMD5Digest;
  81. function GetSHA1OfAnsiString(const S: AnsiString): TSHA1Digest;
  82. function GetSHA1OfUnicodeString(const S: UnicodeString): TSHA1Digest;
  83. procedure ProcessMessagesProc; far;
  84. procedure ExecAndLogOutputLog(const S: String; const Error, FirstLine: Boolean; const Data: NativeInt);
  85. procedure ExecAndLogOutputLogCustom(const S: String; const Error, FirstLine: Boolean; const Data: NativeInt);
  86. function CustomMessage(const MsgName: String): String;
  87. function NewExtractRelativePath(BaseName, DestName: string): string;
  88. function NewFileSearch(const DisableFsRedir: Boolean;
  89. const Name, DirList: String): String;
  90. function GetExceptionMessage(const Caller: TPSExec): String;
  91. function GetCodePreviousData(const ExpandedAppID, ValueName, DefaultValueData: String): String;
  92. function SetCodePreviousData(const PreviousDataKey: HKEY; const ValueName, ValueData: String): Boolean;
  93. function LoadStringFromFile(const FileName: String; var S: AnsiString;
  94. const Sharing: TFileSharing): Boolean;
  95. function LoadStringsFromFile(const FileName: String; const Stack: TPSStack;
  96. const ItemNo: Longint; const Sharing: TFileSharing): Boolean;
  97. function SaveStringToFile(const FileName: String; const S: AnsiString; Append: Boolean): Boolean;
  98. function SaveStringsToFile(const FileName: String; const Stack: TPSStack;
  99. const ItemNo: Longint; Append, UTF8, UTF8WithoutBOM: Boolean): Boolean;
  100. function CreateCallback(const Caller: TPSExec; const P: PPSVariantProcPtr): LongWord;
  101. implementation
  102. uses
  103. Forms, SysUtils, Graphics,
  104. uPSUtils, PathFunc, ASMInline, PSStackHelper,
  105. Setup.MainFunc, SetupLdrAndSetup.RedirFunc, Setup.InstFunc,
  106. SetupLdrAndSetup.Messages, Shared.SetupMessageIDs,
  107. Shared.SetupTypes, Shared.SetupSteps, Setup.LoggingFunc, Setup.SetupForm;
  108. procedure NoUninstallFuncError(const C: AnsiString); overload;
  109. begin
  110. InternalError(Format('Cannot call "%s" function during Uninstall', [C]));
  111. end;
  112. procedure OnlyUninstallFuncError(const C: AnsiString); overload;
  113. begin
  114. InternalError(Format('Cannot call "%s" function during Setup', [C]));
  115. end;
  116. function GetWizardForm: TWizardForm;
  117. begin
  118. Result := WizardForm;
  119. if Result = nil then
  120. InternalError('An attempt was made to access WizardForm before it has been created');
  121. end;
  122. function GetUninstallProgressForm: TUninstallProgressForm;
  123. begin
  124. Result := UninstallProgressForm;
  125. if Result = nil then
  126. InternalError('An attempt was made to access UninstallProgressForm before it has been created');
  127. end;
  128. function GetMsgBoxCaption: String;
  129. var
  130. ID: TSetupMessageID;
  131. begin
  132. if IsUninstaller then
  133. ID := msgUninstallAppTitle
  134. else
  135. ID := msgSetupAppTitle;
  136. Result := SetupMessages[ID];
  137. end;
  138. var
  139. ScaleBaseUnitsInitialized: Boolean;
  140. procedure InitializeScaleBaseUnits;
  141. var
  142. Font: TFont;
  143. begin
  144. if ScaleBaseUnitsInitialized then
  145. Exit;
  146. Font := TFont.Create;
  147. try
  148. SetFontNameSize(Font, LangOptions.DialogFontName, LangOptions.DialogFontSize,
  149. '', 8);
  150. CalculateBaseUnitsFromFont(Font, ScaleBaseUnitX, ScaleBaseUnitY);
  151. finally
  152. Font.Free;
  153. end;
  154. ScaleBaseUnitsInitialized := True;
  155. end;
  156. function IsProtectedSrcExe(const Filename: String): Boolean;
  157. begin
  158. if (MainForm = nil) or (MainForm.CurStep < ssInstall) then begin
  159. var ExpandedFilename := PathExpand(Filename);
  160. Result := PathCompare(ExpandedFilename, SetupLdrOriginalFilename) = 0;
  161. end else
  162. Result := False;
  163. end;
  164. procedure FindDataToFindRec(const FindData: TWin32FindData;
  165. var FindRec: TFindRec);
  166. begin
  167. FindRec.Name := FindData.cFileName;
  168. FindRec.Attributes := FindData.dwFileAttributes;
  169. FindRec.SizeHigh := FindData.nFileSizeHigh;
  170. FindRec.SizeLow := FindData.nFileSizeLow;
  171. FindRec.CreationTime := FindData.ftCreationTime;
  172. FindRec.LastAccessTime := FindData.ftLastAccessTime;
  173. FindRec.LastWriteTime := FindData.ftLastWriteTime;
  174. FindRec.AlternateName := FindData.cAlternateFileName;
  175. end;
  176. function FindFirstHelper(const FileName: String; var FindRec: TFindRec): Boolean;
  177. var
  178. FindHandle: THandle;
  179. FindData: TWin32FindData;
  180. begin
  181. FindHandle := FindFirstFileRedir(ScriptFuncDisableFsRedir, FileName, FindData);
  182. if FindHandle <> INVALID_HANDLE_VALUE then begin
  183. FindRec.FindHandle := FindHandle;
  184. FindDataToFindRec(FindData, FindRec);
  185. Result := True;
  186. end
  187. else begin
  188. FindRec.FindHandle := 0;
  189. Result := False;
  190. end;
  191. end;
  192. function FindNextHelper(var FindRec: TFindRec): Boolean;
  193. var
  194. FindData: TWin32FindData;
  195. begin
  196. Result := (FindRec.FindHandle <> 0) and FindNextFile(FindRec.FindHandle, FindData);
  197. if Result then
  198. FindDataToFindRec(FindData, FindRec);
  199. end;
  200. procedure FindCloseHelper(var FindRec: TFindRec);
  201. begin
  202. if FindRec.FindHandle <> 0 then begin
  203. Windows.FindClose(FindRec.FindHandle);
  204. FindRec.FindHandle := 0;
  205. end;
  206. end;
  207. function FmtMessageHelper(const S: String; const Args: array of String): String;
  208. begin
  209. Result := FmtMessage(PChar(S), Args);
  210. end;
  211. procedure GetWindowsVersionExHelper(var Version: TWindowsVersion);
  212. begin
  213. Version.Major := WindowsVersion shr 24;
  214. Version.Minor := (WindowsVersion shr 16) and $FF;
  215. Version.Build := WindowsVersion and $FFFF;
  216. Version.ServicePackMajor := Hi(NTServicePackLevel);
  217. Version.ServicePackMinor := Lo(NTServicePackLevel);
  218. Version.NTPlatform := True;
  219. Version.ProductType := WindowsProductType;
  220. Version.SuiteMask := WindowsSuiteMask;
  221. end;
  222. procedure CrackCodeRootKey(CodeRootKey: HKEY; var RegView: TRegView;
  223. var RootKey: HKEY);
  224. begin
  225. if (CodeRootKey and not CodeRootKeyValidFlags) = HKEY_AUTO then begin
  226. { Change HKA to HKLM or HKCU, keeping our special flag bits. }
  227. CodeRootKey := (CodeRootKey and CodeRootKeyValidFlags) or InstallModeRootKey;
  228. end else begin
  229. { Allow only predefined key handles (8xxxxxxx). Can't accept handles to
  230. open keys because they might have our special flag bits set.
  231. Also reject unknown flags which may have a meaning in the future. }
  232. if (CodeRootKey shr 31 <> 1) or
  233. ((CodeRootKey and CodeRootKeyFlagMask) and not CodeRootKeyValidFlags <> 0) then
  234. InternalError('Invalid RootKey value');
  235. end;
  236. if CodeRootKey and CodeRootKeyFlag32Bit <> 0 then
  237. RegView := rv32Bit
  238. else if CodeRootKey and CodeRootKeyFlag64Bit <> 0 then begin
  239. if not IsWin64 then
  240. InternalError('Cannot access 64-bit registry keys on this version of Windows');
  241. RegView := rv64Bit;
  242. end
  243. else
  244. RegView := InstallDefaultRegView;
  245. RootKey := CodeRootKey and not CodeRootKeyFlagMask;
  246. end;
  247. function GetSubkeyOrValueNames(const RegView: TRegView; const RootKey: HKEY;
  248. const SubKeyName: String; const Stack: TPSStack; const ItemNo: Longint; const Subkey: Boolean): Boolean;
  249. const
  250. samDesired: array [Boolean] of REGSAM = (KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS);
  251. var
  252. K: HKEY;
  253. Buf, S: String;
  254. BufSize, R: DWORD;
  255. begin
  256. Result := False;
  257. SetString(Buf, nil, 512);
  258. if RegOpenKeyExView(RegView, RootKey, PChar(SubKeyName), 0, samDesired[Subkey], K) <> ERROR_SUCCESS then
  259. Exit;
  260. try
  261. var ArrayBuilder := Stack.InitArrayBuilder(ItemNo);
  262. while True do begin
  263. BufSize := Length(Buf);
  264. if Subkey then
  265. R := RegEnumKeyEx(K, ArrayBuilder.I, @Buf[1], BufSize, nil, nil, nil, nil)
  266. else
  267. R := RegEnumValue(K, ArrayBuilder.I, @Buf[1], BufSize, nil, nil, nil, nil);
  268. case R of
  269. ERROR_SUCCESS: ;
  270. ERROR_NO_MORE_ITEMS: Break;
  271. ERROR_MORE_DATA:
  272. begin
  273. { Double the size of the buffer and try again }
  274. if Length(Buf) >= 65536 then begin
  275. { Sanity check: If we tried a 64 KB buffer and it's still saying
  276. there's more data, something must be seriously wrong. Bail. }
  277. Exit;
  278. end;
  279. SetString(Buf, nil, Length(Buf) * 2);
  280. Continue;
  281. end;
  282. else
  283. Exit; { unknown failure... }
  284. end;
  285. SetString(S, PChar(@Buf[1]), BufSize);
  286. ArrayBuilder.Add(S);
  287. end;
  288. finally
  289. RegCloseKey(K);
  290. end;
  291. Result := True;
  292. end;
  293. function GetMD5OfFile(const DisableFsRedir: Boolean; const Filename: String): TMD5Digest;
  294. { Gets MD5 sum of the file Filename. An exception will be raised upon
  295. failure. }
  296. var
  297. Buf: array[0..65535] of Byte;
  298. begin
  299. var Context: TMD5Context;
  300. MD5Init(Context);
  301. var F := TFileRedir.Create(DisableFsRedir, Filename, fdOpenExisting, faRead, fsReadWrite);
  302. try
  303. while True do begin
  304. var NumRead := F.Read(Buf, SizeOf(Buf));
  305. if NumRead = 0 then
  306. Break;
  307. MD5Update(Context, Buf, NumRead);
  308. end;
  309. finally
  310. F.Free;
  311. end;
  312. Result := MD5Final(Context);
  313. end;
  314. function GetSHA1OfFile(const DisableFsRedir: Boolean; const Filename: String): TSHA1Digest;
  315. { Gets SHA-1 sum of the file Filename. An exception will be raised upon
  316. failure. }
  317. var
  318. Buf: array[0..65535] of Byte;
  319. begin
  320. var Context: TSHA1Context;
  321. SHA1Init(Context);
  322. var F := TFileRedir.Create(DisableFsRedir, Filename, fdOpenExisting, faRead, fsReadWrite);
  323. try
  324. while True do begin
  325. var NumRead := F.Read(Buf, SizeOf(Buf));
  326. if NumRead = 0 then
  327. Break;
  328. SHA1Update(Context, Buf, NumRead);
  329. end;
  330. finally
  331. F.Free;
  332. end;
  333. Result := SHA1Final(Context);
  334. end;
  335. function GetMD5OfAnsiString(const S: AnsiString): TMD5Digest;
  336. begin
  337. Result := MD5Buf(Pointer(S)^, Length(S)*SizeOf(S[1]));
  338. end;
  339. function GetMD5OfUnicodeString(const S: UnicodeString): TMD5Digest;
  340. begin
  341. Result := MD5Buf(Pointer(S)^, Length(S)*SizeOf(S[1]));
  342. end;
  343. function GetSHA1OfAnsiString(const S: AnsiString): TSHA1Digest;
  344. begin
  345. Result := SHA1Buf(Pointer(S)^, Length(S)*SizeOf(S[1]));
  346. end;
  347. function GetSHA1OfUnicodeString(const S: UnicodeString): TSHA1Digest;
  348. begin
  349. Result := SHA1Buf(Pointer(S)^, Length(S)*SizeOf(S[1]));
  350. end;
  351. procedure ProcessMessagesProc; far;
  352. begin
  353. Application.ProcessMessages;
  354. end;
  355. procedure ExecAndLogOutputLog(const S: String; const Error, FirstLine: Boolean; const Data: NativeInt);
  356. begin
  357. Log(S);
  358. end;
  359. procedure ExecAndLogOutputLogCustom(const S: String; const Error, FirstLine: Boolean; const Data: NativeInt);
  360. begin
  361. var OnLog := TOnLog(PMethod(Data)^);
  362. OnLog(S, Error, FirstLine);
  363. end;
  364. function CustomMessage(const MsgName: String): String;
  365. begin
  366. if not GetCustomMessageValue(MsgName, Result) then
  367. InternalError(Format('Unknown custom message name "%s"', [MsgName]));
  368. end;
  369. { ExtractRelativePath is not in Delphi 2's SysUtils. Use the one from Delphi 7.01. }
  370. function NewExtractRelativePath(BaseName, DestName: string): string;
  371. var
  372. BasePath, DestPath: string;
  373. BaseLead, DestLead: PChar;
  374. BasePtr, DestPtr: PChar;
  375. function ExtractFilePathNoDrive(const FileName: string): string;
  376. begin
  377. Result := PathExtractPath(FileName);
  378. Delete(Result, 1, Length(PathExtractDrive(FileName)));
  379. end;
  380. function Next(var Lead: PChar): PChar;
  381. begin
  382. Result := Lead;
  383. if Result = nil then Exit;
  384. Lead := PathStrScan(Lead, '\');
  385. if Lead <> nil then
  386. begin
  387. Lead^ := #0;
  388. Inc(Lead);
  389. end;
  390. end;
  391. begin
  392. { For consistency with the PathExtract* functions, normalize slashes so
  393. that forward slashes and multiple slashes work with this function also }
  394. BaseName := PathNormalizeSlashes(BaseName);
  395. DestName := PathNormalizeSlashes(DestName);
  396. if PathCompare(PathExtractDrive(BaseName), PathExtractDrive(DestName)) = 0 then
  397. begin
  398. BasePath := ExtractFilePathNoDrive(BaseName);
  399. UniqueString(BasePath);
  400. DestPath := ExtractFilePathNoDrive(DestName);
  401. UniqueString(DestPath);
  402. BaseLead := Pointer(BasePath);
  403. BasePtr := Next(BaseLead);
  404. DestLead := Pointer(DestPath);
  405. DestPtr := Next(DestLead);
  406. while (BasePtr <> nil) and (DestPtr <> nil) and (PathCompare(BasePtr, DestPtr) = 0) do
  407. begin
  408. BasePtr := Next(BaseLead);
  409. DestPtr := Next(DestLead);
  410. end;
  411. Result := '';
  412. while BaseLead <> nil do
  413. begin
  414. Result := Result + '..\'; { Do not localize }
  415. Next(BaseLead);
  416. end;
  417. if (DestPtr <> nil) and (DestPtr^ <> #0) then
  418. Result := Result + DestPtr + '\';
  419. if DestLead <> nil then
  420. Result := Result + DestLead; // destlead already has a trailing backslash
  421. Result := Result + PathExtractName(DestName);
  422. end
  423. else
  424. Result := DestName;
  425. end;
  426. { Use our own FileSearch function which includes these improvements over
  427. Delphi's version:
  428. - it supports MBCS and uses Path* functions
  429. - it uses NewFileExistsRedir instead of FileExists
  430. - it doesn't search the current directory unless it's told to
  431. - it always returns a fully-qualified path }
  432. function NewFileSearch(const DisableFsRedir: Boolean;
  433. const Name, DirList: String): String;
  434. var
  435. I, P, L: Integer;
  436. begin
  437. { If Name is absolute, drive-relative, or root-relative, don't search DirList }
  438. if PathDrivePartLengthEx(Name, True) <> 0 then begin
  439. Result := PathExpand(Name);
  440. if NewFileExistsRedir(DisableFsRedir, Result) then
  441. Exit;
  442. end
  443. else begin
  444. P := 1;
  445. L := Length(DirList);
  446. while True do begin
  447. while (P <= L) and (DirList[P] = ';') do
  448. Inc(P);
  449. if P > L then
  450. Break;
  451. I := P;
  452. while (P <= L) and (DirList[P] <> ';') do
  453. Inc(P, PathCharLength(DirList, P));
  454. Result := PathExpand(PathCombine(Copy(DirList, I, P - I), Name));
  455. if NewFileExistsRedir(DisableFsRedir, Result) then
  456. Exit;
  457. end;
  458. end;
  459. Result := '';
  460. end;
  461. function GetExceptionMessage(const Caller: TPSExec): String;
  462. var
  463. Code: TPSError;
  464. E: TObject;
  465. begin
  466. Code := Caller.LastEx;
  467. if Code = erNoError then
  468. Result := '(There is no current exception)'
  469. else begin
  470. E := Caller.LastExObject;
  471. if Assigned(E) and (E is Exception) then
  472. Result := Exception(E).Message
  473. else
  474. Result := String(PSErrorToString(Code, Caller.LastExParam));
  475. end;
  476. end;
  477. function GetCodePreviousData(const ExpandedAppID, ValueName, DefaultValueData: String): String;
  478. begin
  479. { do not localize or change the following string }
  480. Result := GetPreviousData(ExpandedAppId, 'Inno Setup CodeFile: ' + ValueName, DefaultValueData);
  481. end;
  482. { Also see RegisterUninstallInfo in Install.pas }
  483. function SetCodePreviousData(const PreviousDataKey: HKEY; const ValueName, ValueData: String): Boolean;
  484. begin
  485. if ValueData <> '' then begin
  486. { do not localize or change the following string }
  487. Result := RegSetValueEx(PreviousDataKey, PChar('Inno Setup CodeFile: ' + ValueName), 0, REG_SZ, PChar(ValueData), (Length(ValueData)+1)*SizeOf(ValueData[1])) = ERROR_SUCCESS
  488. end else
  489. Result := True;
  490. end;
  491. function LoadStringFromFile(const FileName: String; var S: AnsiString;
  492. const Sharing: TFileSharing): Boolean;
  493. var
  494. F: TFile;
  495. N: Cardinal;
  496. begin
  497. try
  498. F := TFileRedir.Create(ScriptFuncDisableFsRedir, FileName, fdOpenExisting, faRead, Sharing);
  499. try
  500. N := F.CappedSize;
  501. SetLength(S, N);
  502. F.ReadBuffer(S[1], N);
  503. finally
  504. F.Free;
  505. end;
  506. Result := True;
  507. except
  508. Result := False;
  509. end;
  510. end;
  511. function LoadStringsFromFile(const FileName: String; const Stack: TPSStack;
  512. const ItemNo: Longint; const Sharing: TFileSharing): Boolean;
  513. var
  514. F: TTextFileReader;
  515. begin
  516. try
  517. F := TTextFileReaderRedir.Create(ScriptFuncDisableFsRedir, FileName, fdOpenExisting, faRead, Sharing);
  518. try
  519. var ArrayBuilder := Stack.InitArrayBuilder(ItemNo);
  520. while not F.Eof do
  521. ArrayBuilder.Add(F.ReadLine);
  522. finally
  523. F.Free;
  524. end;
  525. Result := True;
  526. except
  527. Result := False;
  528. end;
  529. end;
  530. function SaveStringToFile(const FileName: String; const S: AnsiString; Append: Boolean): Boolean;
  531. var
  532. F: TFile;
  533. begin
  534. try
  535. if Append then
  536. F := TFileRedir.Create(ScriptFuncDisableFsRedir, FileName, fdOpenAlways, faWrite, fsNone)
  537. else
  538. F := TFileRedir.Create(ScriptFuncDisableFsRedir, FileName, fdCreateAlways, faWrite, fsNone);
  539. try
  540. F.SeekToEnd;
  541. F.WriteAnsiString(S);
  542. finally
  543. F.Free;
  544. end;
  545. Result := True;
  546. except
  547. Result := False;
  548. end;
  549. end;
  550. function SaveStringsToFile(const FileName: String; const Stack: TPSStack;
  551. const ItemNo: Longint; Append, UTF8, UTF8WithoutBOM: Boolean): Boolean;
  552. var
  553. F: TTextFileWriter;
  554. begin
  555. try
  556. if Append then
  557. F := TTextFileWriterRedir.Create(ScriptFuncDisableFsRedir, FileName, fdOpenAlways, faWrite, fsNone)
  558. else
  559. F := TTextFileWriterRedir.Create(ScriptFuncDisableFsRedir, FileName, fdCreateAlways, faWrite, fsNone);
  560. try
  561. if UTF8 and UTF8WithoutBOM then
  562. F.UTF8WithoutBOM := UTF8WithoutBOM;
  563. var ArrayEnumerator := Stack.InitArrayEnumerator(ItemNo);
  564. while ArrayEnumerator.HasNext do begin
  565. var S := ArrayEnumerator.Next;
  566. if not UTF8 then
  567. F.WriteAnsiLine(AnsiString(S))
  568. else
  569. F.WriteLine(S);
  570. end;
  571. finally
  572. F.Free;
  573. end;
  574. Result := True;
  575. except
  576. Result := False;
  577. end;
  578. end;
  579. var
  580. ASMInliners: array of Pointer;
  581. function CreateCallback(const Caller: TPSExec; const P: PPSVariantProcPtr): LongWord;
  582. var
  583. ProcRec: TPSInternalProcRec;
  584. Method: TMethod;
  585. Inliner: TASMInline;
  586. ParamCount, SwapFirst, SwapLast: Integer;
  587. S: tbtstring;
  588. begin
  589. { ProcNo 0 means nil was passed by the script }
  590. if P.ProcNo = 0 then
  591. InternalError('Invalid Method value');
  592. { Calculate parameter count of our proc, will need this later. }
  593. ProcRec := Caller.GetProcNo(P.ProcNo) as TPSInternalProcRec;
  594. S := ProcRec.ExportDecl;
  595. GRFW(S);
  596. ParamCount := 0;
  597. while S <> '' do begin
  598. Inc(ParamCount);
  599. GRFW(S);
  600. end;
  601. { Turn our proc into a callable TMethod - its Code will point to
  602. ROPS' MyAllMethodsHandler and its Data to a record identifying our proc.
  603. When called, MyAllMethodsHandler will use the record to call our proc. }
  604. Method := MkMethod(Caller, P.ProcNo);
  605. { Wrap our TMethod with a dynamically generated stdcall callback which will
  606. do two things:
  607. -Remember the Data pointer which MyAllMethodsHandler needs.
  608. -Handle the calling convention mismatch.
  609. Based on InnoCallback by Sherlock Software, see
  610. http://www.sherlocksoftware.org/page.php?id=54 and
  611. https://github.com/thenickdude/InnoCallback. }
  612. Inliner := TASMInline.create;
  613. try
  614. Inliner.Pop(EAX); //get the retptr off the stack
  615. SwapFirst := 2;
  616. SwapLast := ParamCount-1;
  617. //Reverse the order of parameters from param3 onwards in the stack
  618. while SwapLast > SwapFirst do begin
  619. Inliner.Mov(ECX, Inliner.Addr(ESP, SwapFirst * 4)); //load the first item of the pair
  620. Inliner.Mov(EDX, Inliner.Addr(ESP, SwapLast * 4)); //load the last item of the pair
  621. Inliner.Mov(Inliner.Addr(ESP, SwapFirst * 4), EDX);
  622. Inliner.Mov(Inliner.Addr(ESP, SwapLast * 4), ECX);
  623. Inc(SwapFirst);
  624. Dec(SwapLast);
  625. end;
  626. if ParamCount >= 1 then
  627. Inliner.Pop(EDX); //load param1
  628. if ParamCount >= 2 then
  629. Inliner.Pop(ECX); //load param2
  630. Inliner.Push(EAX); //put the retptr back onto the stack
  631. Inliner.Mov(EAX, LongWord(Method.Data)); //Load the self ptr
  632. Inliner.Jmp(Method.Code); //jump to the wrapped proc
  633. SetLength(ASMInliners, Length(ASMInliners) + 1);
  634. ASMInliners[High(ASMInliners)] := Inliner.SaveAsMemory;
  635. Result := LongWord(ASMInliners[High(ASMInliners)]);
  636. finally
  637. Inliner.Free;
  638. end;
  639. end;
  640. procedure FreeASMInliners;
  641. var
  642. I: Integer;
  643. begin
  644. for I := 0 to High(ASMInliners) do
  645. FreeMem(ASMInliners[I]);
  646. SetLength(ASMInliners, 0);
  647. end;
  648. { TProgressThrottler }
  649. constructor TProgressThrottler.Create(const OnDownloadProgress: TOnDownloadProgress);
  650. begin
  651. inherited Create;
  652. FOnDownloadProgress := OnDownloadProgress;
  653. end;
  654. constructor TProgressThrottler.Create(const OnExtractionProgress: TOnExtractionProgress);
  655. begin
  656. inherited Create;
  657. FOnExtractionProgress := OnExtractionProgress;
  658. end;
  659. procedure TProgressThrottler.Reset;
  660. begin
  661. FStopWatch.Stop;
  662. end;
  663. function TProgressThrottler.ThrottleOk(const Progress, ProgressMax: Int64): Boolean;
  664. begin
  665. if FStopWatch.IsRunning then begin
  666. Result := ((Progress = ProgressMax) and (FLastOkProgress <> ProgressMax)) or (FStopWatch.ElapsedMilliseconds >= 50);
  667. if Result then
  668. FStopWatch.Reset;
  669. end else begin
  670. Result := True;
  671. FStopWatch := TStopwatch.StartNew;
  672. end;
  673. if Result then
  674. FLastOkProgress := Progress;
  675. end;
  676. function TProgressThrottler.OnDownloadProgress(const Url, BaseName: string; const Progress,
  677. ProgressMax: Int64): Boolean;
  678. begin
  679. if Assigned(FOnDownloadProgress) and ThrottleOk(Progress, ProgressMax) then begin
  680. Result := FOnDownloadProgress(Url, BaseName, Progress, ProgressMax)
  681. end else
  682. Result := True;
  683. end;
  684. function TProgressThrottler.OnExtractionProgress(const ArchiveName, FileName: string;
  685. const Progress, ProgressMax: Int64): Boolean;
  686. begin
  687. if Assigned(FOnExtractionProgress) and ThrottleOk(Progress, ProgressMax) then
  688. Result := FOnExtractionProgress(ArchiveName, FileName, Progress, ProgressMax)
  689. else
  690. Result := True;
  691. end;
  692. initialization
  693. finalization
  694. FreeASMInliners;
  695. end.