/packages/fcl-res/src/versionresource.pp

https://github.com/slibre/freepascal · Puppet · 593 lines · 526 code · 67 blank · 0 comment · 26 complexity · 6aa2114f2552950f17c280ea7387d109 MD5 · raw file

  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by Giulio Bernardi
  4. Version information resource type
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit versionresource;
  12. {$MODE OBJFPC}
  13. interface
  14. uses
  15. SysUtils, Classes, resource, versiontypes;
  16. type
  17. TVerBlockHeader = packed record
  18. length : word;
  19. vallength : word;
  20. valtype : word;
  21. key : string;
  22. end;
  23. type
  24. { TVersionResource }
  25. TVersionResource = class(TAbstractResource)
  26. private
  27. fType : TResourceDesc;
  28. fName : TResourceDesc;
  29. fFixedInfo : TVersionFixedInfo;
  30. fStringFileInfo : TVersionStringFileInfo;
  31. fVarFileInfo : TVersionVarFileInfo;
  32. function SwapVersion(const aData : qword) : qword;
  33. procedure AlignDWordReading;
  34. procedure AlignDWordWriting;
  35. function GetFileInfo : TVersionStringFileInfo;
  36. function GetFixedInfo : TVersionFixedInfo;
  37. function GetVarInfo : TVersionVarFileInfo;
  38. procedure CheckDataLoaded;
  39. procedure LoadData;
  40. procedure LoadFixedInfos;
  41. function ReadBlockHeader(out aBlock : TVerBlockHeader) : integer;
  42. function ReadStringFileInfo(toread : integer) : integer;
  43. function ReadVarFileInfo(toread : integer) : integer;
  44. function ReadStringTable(toread : integer;aName : string) : integer;
  45. function ReadWideString: string;
  46. procedure WriteFixedBlockLength(const position : int64);
  47. procedure WriteData;
  48. procedure WriteFixedInfos;
  49. procedure WriteStringFileInfo;
  50. procedure WriteStringTable(aTable : TVersionStringTable);
  51. procedure WriteStringEntry(const aKey, aValue : string);
  52. procedure WriteVarFileInfo;
  53. procedure WriteVarEntry(aEntry : TVerTranslationInfo);
  54. procedure WriteWideString(const aString : string);
  55. protected
  56. function GetType : TResourceDesc; override;
  57. function GetName : TResourceDesc; override;
  58. function ChangeDescTypeAllowed(aDesc : TResourceDesc) : boolean; override;
  59. function ChangeDescValueAllowed(aDesc : TResourceDesc) : boolean; override;
  60. procedure NotifyResourcesLoaded; override;
  61. public
  62. constructor Create; override;
  63. constructor Create(aType,aName : TResourceDesc); override;
  64. destructor Destroy; override;
  65. procedure UpdateRawData; override;
  66. property FixedInfo : TVersionFixedInfo read GetFixedInfo;
  67. property StringFileInfo : TVersionStringFileInfo read GetFileInfo;
  68. property VarFileInfo : TVersionVarFileInfo read GetVarInfo;
  69. end;
  70. implementation
  71. uses
  72. resfactory;
  73. type
  74. TVSFixedFileInfo = packed record
  75. signature : longword;
  76. verMinor : word;
  77. verMajor : word;
  78. FileVersion : qword;
  79. ProductVersion : qword;
  80. FileFlagsMask : longword;
  81. FileFlags : longword;
  82. FileOS : longword;
  83. FileType : longword;
  84. FileSubType : longword;
  85. FileDate : qword;
  86. end;
  87. { TVersionResource }
  88. function TVersionResource.SwapVersion(const aData: qword): qword;
  89. begin
  90. {$IFDEF ENDIAN_BIG}
  91. Result:=Swap(aData); //MSLW is stored first...
  92. Result:=SwapEndian(Result);
  93. {$ELSE}
  94. Result:=Swap(longword(hi(aData)));
  95. Result:=Result shl 32;
  96. Result:=Result or Swap(longword(lo(aData)));
  97. {$ENDIF}
  98. end;
  99. procedure TVersionResource.AlignDWordReading;
  100. var toskip : integer;
  101. begin
  102. toskip:=4-(RawData.Position mod 4);
  103. if toskip<>4 then RawData.Seek(toskip,soFromCurrent);
  104. end;
  105. procedure TVersionResource.AlignDWordWriting;
  106. var topad : integer;
  107. lw : longword;
  108. begin
  109. lw:=0;
  110. topad:=4-(RawData.Position mod 4);
  111. if topad<>4 then RawData.WriteBuffer(lw,topad);
  112. end;
  113. function TVersionResource.GetFileInfo: TVersionStringFileInfo;
  114. begin
  115. CheckDataLoaded;
  116. Result:=fStringFileInfo;
  117. end;
  118. function TVersionResource.GetFixedInfo: TVersionFixedInfo;
  119. begin
  120. CheckDataLoaded;
  121. Result:=fFixedInfo;
  122. end;
  123. function TVersionResource.GetVarInfo: TVersionVarFileInfo;
  124. begin
  125. CheckDataLoaded;
  126. Result:=fVarFileInfo;
  127. end;
  128. procedure TVersionResource.CheckDataLoaded;
  129. begin
  130. if fFixedInfo<>nil then exit;
  131. //if we have no data stream, create empty structures
  132. if RawData.Size=0 then
  133. begin
  134. fFixedInfo:=TVersionFixedInfo.Create;
  135. fStringFileInfo:=TVersionStringFileInfo.Create;
  136. fVarFileInfo:=TVersionVarFileInfo.Create;
  137. end
  138. else LoadData;
  139. end;
  140. procedure TVersionResource.LoadData;
  141. var tmp : integer;
  142. toread : word;
  143. block : TVerBlockHeader;
  144. i : integer;
  145. begin
  146. RawData.Position:=0;
  147. tmp:=ReadBlockHeader(block);
  148. //block.key should be 'VS_VERSION_INFO'
  149. toread:=block.length;
  150. LoadFixedInfos;
  151. AlignDWordReading;
  152. dec(toread,RawData.Position);
  153. fStringFileInfo:=TVersionStringFileInfo.Create;
  154. fVarFileInfo:=TVersionVarFileInfo.Create;
  155. for i:=1 to 2 do
  156. begin
  157. if toread<=0 then exit;
  158. tmp:=ReadBlockHeader(block);
  159. dec(toread,tmp);
  160. if block.key='StringFileInfo' then tmp:=ReadStringFileInfo(block.length-tmp)
  161. else if block.key='VarFileInfo' then tmp:=ReadVarFileInfo(block.length-tmp);
  162. dec(toread,tmp);
  163. end;
  164. end;
  165. procedure TVersionResource.LoadFixedInfos;
  166. var infodata : TVSFixedFileInfo;
  167. begin
  168. RawData.ReadBuffer(infodata,sizeof(infodata));
  169. infodata.FileVersion:=SwapVersion(infodata.FileVersion);
  170. infodata.ProductVersion:=SwapVersion(infodata.ProductVersion);
  171. infodata.FileDate:=Swap(infodata.FileDate); //MSLW is stored first...
  172. {$IFDEF ENDIAN_BIG}
  173. infodata.signature:=SwapEndian(infodata.signature);
  174. infodata.verMinor:=SwapEndian(infodata.verMinor);
  175. infodata.verMajor:=SwapEndian(infodata.verMajor);
  176. infodata.FileFlagsMask:=SwapEndian(infodata.FileFlagsMask);
  177. infodata.FileFlags:=SwapEndian(infodata.FileFlags);
  178. infodata.FileOS:=SwapEndian(infodata.FileOS);
  179. infodata.FileType:=SwapEndian(infodata.FileType);
  180. infodata.FileSubType:=SwapEndian(infodata.FileSubType);
  181. infodata.FileDate:=SwapEndian(infodata.FileDate);
  182. {$ENDIF}
  183. fFixedInfo:=TVersionFixedInfo.Create;
  184. fFixedInfo.FileVersion:=TFileProductVersion(infodata.FileVersion);
  185. fFixedInfo.ProductVersion:=TFileProductVersion(infodata.ProductVersion);
  186. fFixedInfo.FileFlagsMask:=infodata.FileFlagsMask;
  187. fFixedInfo.FileFlags:=infodata.FileFlags;
  188. fFixedInfo.FileOS:=infodata.FileOS;
  189. fFixedInfo.FileType:=infodata.FileType;
  190. fFixedInfo.FileSubType:=infodata.FileSubType;
  191. fFixedInfo.FileDate:=infodata.FileDate;
  192. end;
  193. function TVersionResource.ReadBlockHeader(out aBlock: TVerBlockHeader
  194. ): integer;
  195. var before : int64;
  196. begin
  197. before:=RawData.Position;
  198. RawData.ReadBuffer(aBlock,6);
  199. {$IFDEF ENDIAN_BIG}
  200. aBlock.length:=SwapEndian(aBlock.length);
  201. aBlock.vallength:=SwapEndian(aBlock.vallength);
  202. aBlock.valtype:=SwapEndian(aBlock.valtype);
  203. {$ENDIF}
  204. aBlock.key:=ReadWideString;
  205. AlignDWordReading;
  206. Result:=RawData.Position-before;
  207. end;
  208. function TVersionResource.ReadStringFileInfo(toread : integer) : integer;
  209. var block : TVerBlockHeader;
  210. tmp : integer;
  211. begin
  212. Result:=0;
  213. while toread>0 do
  214. begin
  215. tmp:=ReadBlockHeader(block);
  216. dec(toread,tmp); inc(Result,tmp);
  217. tmp:=ReadStringTable(block.length-tmp,block.key);
  218. dec(toread,tmp); inc(Result,tmp);
  219. end;
  220. end;
  221. function TVersionResource.ReadVarFileInfo(toread : integer) : integer;
  222. var block : TVerBlockHeader;
  223. tmp : integer;
  224. vinfo : TVerTranslationInfo;
  225. before : int64;
  226. begin
  227. Result:=0;
  228. while toread>0 do
  229. begin
  230. before:=RawData.Position;
  231. ReadBlockHeader(block);
  232. if (block.valtype<>0) or (block.key<>'Translation') then
  233. RawData.Seek(block.vallength,soFromCurrent)
  234. else
  235. begin
  236. RawData.ReadBuffer(vinfo,sizeof(vinfo));
  237. {$IFDEF ENDIAN_BIG}
  238. vinfo.language:=SwapEndian(vinfo.language);
  239. vinfo.codepage:=SwapEndian(vinfo.codepage);
  240. {$ENDIF}
  241. fVarFileInfo.Add(vinfo);
  242. end;
  243. AlignDWordReading;
  244. tmp:=RawData.Position-before;
  245. dec(toread,tmp); inc(Result,tmp);
  246. end;
  247. end;
  248. function TVersionResource.ReadStringTable(toread: integer; aName: string
  249. ): integer;
  250. var strtable : TVersionStringTable;
  251. tmp : integer;
  252. block : TVerBlockHeader;
  253. value : string;
  254. before : int64;
  255. begin
  256. Result:=0;
  257. strtable:=TVersionStringTable.Create(aName);
  258. fStringFileInfo.Add(strtable);
  259. while toread>0 do
  260. begin
  261. before:=RawData.Position;
  262. ReadBlockHeader(block);
  263. value:=ReadWideString;
  264. AlignDWordReading;
  265. tmp:=RawData.Position-before;
  266. dec(toread,tmp); inc(Result,tmp);
  267. strtable.Add(block.key,value);
  268. end;
  269. end;
  270. function TVersionResource.ReadWideString: string;
  271. var w : word;
  272. ws : widestring;
  273. begin
  274. ws:='';
  275. w:=0;
  276. repeat
  277. RawData.ReadBuffer(w,2);
  278. if w = 0 then break;
  279. {$IFDEF ENDIAN_BIG}
  280. w:=SwapEndian(w);
  281. {$ENDIF}
  282. ws:=ws+widechar(w);
  283. until false;
  284. Result:=ws;
  285. end;
  286. procedure TVersionResource.WriteFixedBlockLength(const position: int64);
  287. var after : int64;
  288. len : word;
  289. begin
  290. after:=RawData.Position;
  291. len:=after-position;
  292. {$IFDEF ENDIAN_BIG}
  293. len:=SwapEndian(len);
  294. {$ENDIF}
  295. RawData.Position:=position;
  296. RawData.WriteBuffer(len,2);
  297. RawData.Position:=after;
  298. end;
  299. procedure TVersionResource.WriteData;
  300. var block : TVerBlockHeader;
  301. begin
  302. RawData.Size:=0;
  303. RawData.Position:=0;
  304. block.length:=0;
  305. block.vallength:=$34;
  306. block.valtype:=0;
  307. block.key:='VS_VERSION_INFO';
  308. {$IFDEF ENDIAN_BIG}
  309. block.vallength:=SwapEndian(block.vallength);
  310. block.valtype:=SwapEndian(block.valtype);
  311. {$ENDIF}
  312. RawData.WriteBuffer(block,6);
  313. WriteWideString(block.key);
  314. AlignDWordWriting;
  315. WriteFixedInfos;
  316. AlignDWordWriting;
  317. if fStringFileInfo.Count>0 then WriteStringFileInfo;
  318. if fVarFileInfo.Count>0 then WriteVarFileInfo;
  319. WriteFixedBlockLength(0);
  320. end;
  321. procedure TVersionResource.WriteFixedInfos;
  322. var infodata : TVSFixedFileInfo;
  323. begin
  324. infodata.signature:=$FEEF04BD;
  325. infodata.verMinor:=0;
  326. infodata.verMajor:=1;
  327. infodata.FileVersion:=qword(fFixedInfo.FileVersion);
  328. infodata.ProductVersion:=qword(fFixedInfo.ProductVersion);
  329. infodata.FileFlagsMask:=fFixedInfo.FileFlagsMask;
  330. infodata.FileFlags:=fFixedInfo.FileFlags;
  331. infodata.FileOS:=fFixedInfo.FileOS;
  332. infodata.FileType:=fFixedInfo.FileType;
  333. infodata.FileSubType:=fFixedInfo.FileSubType;
  334. infodata.FileDate:=fFixedInfo.FileDate;
  335. {$IFDEF ENDIAN_BIG}
  336. infodata.signature:=SwapEndian(infodata.signature);
  337. infodata.verMinor:=SwapEndian(infodata.verMinor);
  338. infodata.verMajor:=SwapEndian(infodata.verMajor);
  339. infodata.FileFlagsMask:=SwapEndian(infodata.FileFlagsMask);
  340. infodata.FileFlags:=SwapEndian(infodata.FileFlags);
  341. infodata.FileOS:=SwapEndian(infodata.FileOS);
  342. infodata.FileType:=SwapEndian(infodata.FileType);
  343. infodata.FileSubType:=SwapEndian(infodata.FileSubType);
  344. infodata.FileDate:=SwapEndian(infodata.FileDate);
  345. {$ENDIF}
  346. infodata.FileVersion:=SwapVersion(infodata.FileVersion);
  347. infodata.ProductVersion:=SwapVersion(infodata.ProductVersion);
  348. infodata.FileDate:=Swap(infodata.FileDate); //MSLW is stored first...
  349. RawData.WriteBuffer(infodata,sizeof(infodata));
  350. end;
  351. procedure TVersionResource.WriteStringFileInfo;
  352. var block : TVerBlockHeader;
  353. i : integer;
  354. before : int64;
  355. begin
  356. before:=RawData.Position;
  357. block.length:=0;
  358. block.vallength:=0;
  359. block.valtype:=1;
  360. block.key:='StringFileInfo';
  361. {$IFDEF ENDIAN_BIG}
  362. block.vallength:=SwapEndian(block.vallength);
  363. block.valtype:=SwapEndian(block.valtype);
  364. {$ENDIF}
  365. RawData.WriteBuffer(block,6);
  366. WriteWideString(block.key);
  367. AlignDWordWriting;
  368. for i:=0 to fStringFileInfo.Count-1 do
  369. WriteStringTable(fStringFileInfo[i]);
  370. WriteFixedBlockLength(before);
  371. end;
  372. procedure TVersionResource.WriteStringTable(aTable: TVersionStringTable);
  373. var block : TVerBlockHeader;
  374. i : integer;
  375. before : int64;
  376. begin
  377. before:=RawData.Position;
  378. block.length:=0;
  379. block.vallength:=0;
  380. block.valtype:=1;
  381. block.key:=aTable.Name;
  382. {$IFDEF ENDIAN_BIG}
  383. block.vallength:=SwapEndian(block.vallength);
  384. block.valtype:=SwapEndian(block.valtype);
  385. {$ENDIF}
  386. RawData.WriteBuffer(block,6);
  387. WriteWideString(block.key);
  388. AlignDWordWriting;
  389. for i:=0 to aTable.Count-1 do
  390. WriteStringEntry(aTable.Keys[i],aTable.ValuesByIndex[i]);
  391. WriteFixedBlockLength(before);
  392. end;
  393. procedure TVersionResource.WriteStringEntry(const aKey, aValue: string);
  394. var block : TVerBlockHeader;
  395. before: int64;
  396. begin
  397. before:=RawData.Position;
  398. block.length:=0;
  399. block.vallength:=length(aValue)+1;
  400. block.valtype:=1;
  401. block.key:=aKey;
  402. {$IFDEF ENDIAN_BIG}
  403. block.vallength:=SwapEndian(block.vallength);
  404. block.valtype:=SwapEndian(block.valtype);
  405. {$ENDIF}
  406. RawData.WriteBuffer(block,6);
  407. WriteWideString(block.key);
  408. AlignDWordWriting;
  409. WriteWideString(aValue);
  410. AlignDWordWriting;
  411. WriteFixedBlockLength(before);
  412. end;
  413. procedure TVersionResource.WriteVarFileInfo;
  414. var block : TVerBlockHeader;
  415. i : integer;
  416. before : int64;
  417. begin
  418. before:=RawData.Position;
  419. block.length:=0;
  420. block.vallength:=0;
  421. block.valtype:=1;
  422. block.key:='VarFileInfo';
  423. {$IFDEF ENDIAN_BIG}
  424. block.vallength:=SwapEndian(block.vallength);
  425. block.valtype:=SwapEndian(block.valtype);
  426. {$ENDIF}
  427. RawData.WriteBuffer(block,6);
  428. WriteWideString(block.key);
  429. AlignDWordWriting;
  430. for i:=0 to fVarFileInfo.Count-1 do
  431. WriteVarEntry(fVarFileInfo[i]);
  432. WriteFixedBlockLength(before);
  433. end;
  434. procedure TVersionResource.WriteVarEntry(aEntry: TVerTranslationInfo);
  435. var block : TVerBlockHeader;
  436. before: int64;
  437. begin
  438. before:=RawData.Position;
  439. block.length:=0;
  440. block.vallength:=4;
  441. block.valtype:=0;
  442. block.key:='Translation';
  443. {$IFDEF ENDIAN_BIG}
  444. block.vallength:=SwapEndian(block.vallength);
  445. block.valtype:=SwapEndian(block.valtype);
  446. aEntry.language:=SwapEndian(aEntry.language);
  447. aEntry.codepage:=SwapEndian(aEntry.codepage);
  448. {$ENDIF}
  449. RawData.WriteBuffer(block,6);
  450. WriteWideString(block.key);
  451. AlignDWordWriting;
  452. RawData.WriteBuffer(aEntry,sizeof(aEntry));
  453. WriteFixedBlockLength(before);
  454. end;
  455. procedure TVersionResource.WriteWideString(const aString: string);
  456. var ws : widestring;
  457. w : word;
  458. i : integer;
  459. begin
  460. ws:=aString;
  461. for i:=1 to length(ws) do
  462. begin
  463. w:=word(ws[i]);
  464. {$IFDEF ENDIAN_BIG}
  465. w:=SwapEndian(w);
  466. {$ENDIF}
  467. RawData.WriteBuffer(w,2);
  468. end;
  469. w:=0;
  470. RawData.WriteBuffer(w,2);
  471. end;
  472. function TVersionResource.GetType: TResourceDesc;
  473. begin
  474. Result:=fType;
  475. end;
  476. function TVersionResource.GetName: TResourceDesc;
  477. begin
  478. Result:=fName;
  479. end;
  480. function TVersionResource.ChangeDescTypeAllowed(aDesc: TResourceDesc): boolean;
  481. begin
  482. Result:=false;
  483. end;
  484. function TVersionResource.ChangeDescValueAllowed(aDesc: TResourceDesc
  485. ): boolean;
  486. begin
  487. Result:=false;
  488. end;
  489. procedure TVersionResource.NotifyResourcesLoaded;
  490. begin
  491. end;
  492. constructor TVersionResource.Create;
  493. begin
  494. inherited Create;
  495. fType:=TResourceDesc.Create(RT_VERSION);
  496. fName:=TResourceDesc.Create(1);
  497. SetDescOwner(fType);
  498. SetDescOwner(fName);
  499. fFixedInfo:=nil;
  500. fStringFileInfo:=nil;
  501. fVarFileInfo:=nil;
  502. end;
  503. constructor TVersionResource.Create(aType, aName: TResourceDesc);
  504. begin
  505. Create;
  506. end;
  507. destructor TVersionResource.Destroy;
  508. begin
  509. fType.Free;
  510. fName.Free;
  511. if fFixedInfo<>nil then fFixedInfo.Free;
  512. if fStringFileInfo<>nil then fStringFileInfo.Free;
  513. if fVarFileInfo<>nil then fVarFileInfo.Free;
  514. inherited Destroy;
  515. end;
  516. procedure TVersionResource.UpdateRawData;
  517. begin
  518. if fFixedInfo=nil then exit;
  519. WriteData;
  520. FreeAndNil(fFixedInfo);
  521. FreeAndNil(fStringFileInfo);
  522. FreeAndNil(fVarFileInfo);
  523. end;
  524. initialization
  525. TResourceFactory.RegisterResourceClass(RT_VERSION,TVersionResource);
  526. end.