2
0

resourcetree.pp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by Giulio Bernardi
  4. Implements an ordered tree of resources
  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 resourcetree;
  12. {$MODE OBJFPC}
  13. interface
  14. uses
  15. Classes, SysUtils, resource;
  16. type
  17. { TResourceTreeNode }
  18. TResourceTreeNode = class
  19. protected
  20. fParent : TResourceTreeNode;
  21. fNamedEntries : TFPList;
  22. fIDEntries : TFPList;
  23. fSubDirRVA : longword;
  24. fDataRVA : longword;
  25. fNameRva : longword;
  26. fDesc : TResourceDesc;
  27. function GetNamedCount : longword;
  28. function GetNamedEntry(index : integer) : TResourceTreeNode;
  29. function GetIDCount : longword;
  30. function GetIDEntry(index : integer) : TResourceTreeNode;
  31. function GetData : TAbstractResource; virtual;
  32. function InternalFind(aList : TFPList; aDesc : TResourceDesc; out index : integer) : boolean; overload;
  33. function InternalFind(aList : TFPList; aLangID : TLangID; out index : integer) : boolean; overload;
  34. function InternalFind(aType, aName : TResourceDesc; const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource; virtual; abstract; overload;
  35. constructor Create; virtual; overload;
  36. property Parent : TResourceTreeNode read fParent;
  37. public
  38. destructor Destroy; override;
  39. procedure Add(aResource : TAbstractResource); virtual; abstract;
  40. function CreateSubNode(aDesc : TResourceDesc) : TResourceTreeNode; virtual; abstract;
  41. function CreateResource : TAbstractResource; virtual;
  42. procedure Clear;
  43. function Remove(aType, aName : TResourceDesc) : TAbstractResource; overload;
  44. function Remove(aType, aName : TResourceDesc; const aLangID : TLangID) : TAbstractResource; overload;
  45. function Find(aType, aName : TResourceDesc) : TAbstractResource; overload;
  46. function Find(aType, aName : TResourceDesc; const aLangID : TLangID) : TAbstractResource; overload;
  47. function FindFreeID(aType : TResourceDesc) : TResID; virtual;
  48. function IsLeaf : boolean; virtual;
  49. property Desc : TResourceDesc read fDesc;
  50. property NamedCount : longword read GetNamedCount;
  51. property NamedEntries[index : integer] : TResourceTreeNode read GetNamedEntry;
  52. property IDCount : longword read GetIDCount;
  53. property IDEntries[index : integer] : TResourceTreeNode read GetIDEntry;
  54. property NameRVA : longword read fNameRVA write fNameRVA;
  55. property SubDirRVA : longword read fSubDirRVA write fSubDirRVA;
  56. property DataRVA : longword read fDataRVA write fDataRVA;
  57. property Data : TAbstractResource read GetData;
  58. end;
  59. { TRootResTreeNode }
  60. TRootResTreeNode = class (TResourceTreeNode)
  61. protected
  62. function InternalFind(aType, aName : TResourceDesc; const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource; override;
  63. public
  64. constructor Create; override;
  65. function CreateSubNode(aDesc : TResourceDesc) : TResourceTreeNode; override;
  66. procedure Add(aResource : TAbstractResource); override;
  67. function FindFreeID(aType : TResourceDesc) : TResID; override;
  68. end;
  69. implementation
  70. uses resfactory;
  71. { TTypeResTreeNode }
  72. type
  73. TTypeResTreeNode = class (TResourceTreeNode)
  74. protected
  75. function InternalFind(aType, aName : TResourceDesc; const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource; override;
  76. public
  77. constructor Create(aType : TResourceDesc; aParent : TResourceTreeNode); overload;
  78. function CreateSubNode(aDesc : TResourceDesc) : TResourceTreeNode; override;
  79. procedure Add(aResource : TAbstractResource); override;
  80. function FindFreeID(aType : TResourceDesc) : TResID; override;
  81. end;
  82. { TNameResTreeNode }
  83. TNameResTreeNode = class (TResourceTreeNode)
  84. protected
  85. function InternalFind(aType, aName : TResourceDesc; const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource; override;
  86. public
  87. constructor Create(aName : TResourceDesc; aParent : TResourceTreeNode); overload;
  88. function CreateSubNode(aDesc : TResourceDesc) : TResourceTreeNode; override;
  89. procedure Add(aResource : TAbstractResource); override;
  90. end;
  91. { TLangIDResTreeNode }
  92. TLangIDResTreeNode = class (TResourceTreeNode)
  93. private
  94. fData : TAbstractResource;
  95. protected
  96. function GetData : TAbstractResource; override;
  97. function InternalFind(aType, aName : TResourceDesc; const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource; override;
  98. public
  99. constructor Create(aLangID : TLangID; aResource : TAbstractResource; aParent : TResourceTreeNode); overload;
  100. function CreateResource : TAbstractResource; override;
  101. function CreateSubNode(aDesc : TResourceDesc) : TResourceTreeNode; override;
  102. procedure Add(aResource : TAbstractResource); override;
  103. function IsLeaf : boolean; override;
  104. end;
  105. function CompareDesc(desc1, desc2 : TResourceDesc) : integer;
  106. begin
  107. if desc1.DescType=desc2.DescType then
  108. begin
  109. case desc1.DescType of
  110. dtID : Result:=desc1.ID - desc2.ID;
  111. dtName : Result:=CompareStr(desc1.Name,desc2.Name);
  112. end;
  113. end
  114. else
  115. case desc1.DescType of
  116. dtID : Result:=1;
  117. dtName : Result:=-1;
  118. end;
  119. end;
  120. function ResListCompare(Item1: Pointer;Item2: Pointer) : Integer;
  121. var node1, node2 : TResourceTreeNode;
  122. begin
  123. node1:=TResourceTreeNode(Item1);
  124. node2:=TResourceTreeNode(Item2);
  125. Result:=CompareDesc(node1.Desc,node2.Desc);
  126. end;
  127. { TResourceTreeNode }
  128. function TResourceTreeNode.GetIDEntry(index : integer): TResourceTreeNode;
  129. begin
  130. Result:=TResourceTreeNode(fIDEntries[index]);
  131. end;
  132. function TResourceTreeNode.GetData: TAbstractResource;
  133. begin
  134. Result:=nil;
  135. end;
  136. function TResourceTreeNode.InternalFind(aList: TFPList; aDesc: TResourceDesc; out index: integer
  137. ): boolean;
  138. var l, r, p,res : integer;
  139. begin
  140. Result:=true;
  141. l:=0;
  142. r:=aList.Count-1;
  143. while l<=r do
  144. begin
  145. p:=(l+r) div 2;
  146. res:=CompareDesc(TResourceTreeNode(aList[p]).Desc, aDesc);
  147. if res<0 then l:=p+1
  148. else if res>0 then r:=p-1
  149. else if res=0 then
  150. begin
  151. index:=p;
  152. exit;
  153. end;
  154. end;
  155. index:=l; //the item can be inserted here
  156. Result:=false;
  157. end;
  158. function TResourceTreeNode.InternalFind(aList: TFPList; aLangID: TLangID; out
  159. index: integer): boolean;
  160. var l, r, p,res : integer;
  161. begin
  162. Result:=true;
  163. l:=0;
  164. r:=aList.Count-1;
  165. while l<=r do
  166. begin
  167. p:=(l+r) div 2;
  168. if TResourceTreeNode(aList[p]).Desc.DescType=dtName then res:=-1
  169. else res:=TResourceTreeNode(aList[p]).Desc.ID - aLangID;
  170. if res<0 then l:=p+1
  171. else if res>0 then r:=p-1
  172. else if res=0 then
  173. begin
  174. index:=p;
  175. exit;
  176. end;
  177. end;
  178. index:=l; //the item can be inserted here
  179. Result:=false;
  180. end;
  181. function TResourceTreeNode.GetNamedEntry(index : integer): TResourceTreeNode;
  182. begin
  183. Result:=TResourceTreeNode(fNamedEntries[index]);
  184. end;
  185. function TResourceTreeNode.GetNamedCount: longword;
  186. begin
  187. Result:=fNamedEntries.Count;
  188. end;
  189. function TResourceTreeNode.GetIDCount: longword;
  190. begin
  191. Result:=fIDEntries.Count;
  192. end;
  193. constructor TResourceTreeNode.Create;
  194. begin
  195. fDesc:=TResourceDesc.Create(0);
  196. fNamedEntries:=TFPList.Create;
  197. fIDEntries:=TFPList.Create;
  198. fNameRVA:=0;
  199. fSubDirRva:=0;
  200. fDataRVA:=0;
  201. fParent:=nil;
  202. end;
  203. destructor TResourceTreeNode.Destroy;
  204. begin
  205. fDesc.Free;
  206. Clear;
  207. fNamedEntries.Free;
  208. fIDEntries.Free;
  209. end;
  210. function TResourceTreeNode.CreateResource: TAbstractResource;
  211. begin
  212. Result:=nil;
  213. end;
  214. procedure TResourceTreeNode.Clear;
  215. var i : integer;
  216. begin
  217. for i:=0 to fNamedEntries.Count-1 do
  218. TResourceTreeNode(fNamedEntries[i]).Free;
  219. for i:=0 to fIDEntries.Count-1 do
  220. TResourceTreeNode(fIDEntries[i]).Free;
  221. fNamedEntries.Clear;
  222. fIDEntries.Clear;
  223. end;
  224. function TResourceTreeNode.Remove(aType, aName: TResourceDesc
  225. ): TAbstractResource;
  226. begin
  227. Result:=InternalFind(aType,aName,0,true,true);
  228. end;
  229. function TResourceTreeNode.Remove(aType, aName: TResourceDesc;
  230. const aLangID: TLangID): TAbstractResource;
  231. begin
  232. Result:=InternalFind(aType,aName,aLangID,false,true);
  233. end;
  234. function TResourceTreeNode.Find(aType, aName: TResourceDesc
  235. ): TAbstractResource;
  236. begin
  237. Result:=InternalFind(aType,aName,0,true,false);
  238. end;
  239. function TResourceTreeNode.Find(aType, aName: TResourceDesc;
  240. const aLangID: TLangID): TAbstractResource;
  241. begin
  242. Result:=InternalFind(aType,aName,aLangID,false,false);
  243. end;
  244. function TResourceTreeNode.FindFreeID(aType: TResourceDesc): TResID;
  245. begin
  246. Result:=0;
  247. end;
  248. function TResourceTreeNode.IsLeaf: boolean;
  249. begin
  250. Result:=false;
  251. end;
  252. { TRootResTreeNode }
  253. constructor TRootResTreeNode.Create;
  254. begin
  255. inherited Create;
  256. end;
  257. function TRootResTreeNode.CreateSubNode(aDesc: TResourceDesc
  258. ): TResourceTreeNode;
  259. var theList : TFPList;
  260. begin
  261. case aDesc.DescType of
  262. dtID : theList:=fIDEntries;
  263. dtName : theList:=fNamedEntries;
  264. end;
  265. Result:=TTypeResTreeNode.Create(aDesc,self);
  266. thelist.Add(Result);
  267. end;
  268. procedure TRootResTreeNode.Add(aResource: TAbstractResource);
  269. var theList : TFPList;
  270. idx : integer;
  271. subitem : TResourceTreeNode;
  272. begin
  273. case aResource._Type.DescType of
  274. dtID : theList:=fIDEntries;
  275. dtName : theList:=fNamedEntries;
  276. end;
  277. if InternalFind(theList,aResource._Type,idx) then
  278. subitem:=TResourceTreeNode(theList[idx])
  279. else
  280. begin
  281. subitem:=TTypeResTreeNode.Create(aResource._Type,self);
  282. theList.Insert(idx,subitem);
  283. end;
  284. subitem.Add(aResource);
  285. end;
  286. function TRootResTreeNode.FindFreeID(aType: TResourceDesc): TResID;
  287. var theList : TFPList;
  288. idx : integer;
  289. subitem : TResourceTreeNode;
  290. begin
  291. Result:=1;
  292. case aType.DescType of
  293. dtID : theList:=fIDEntries;
  294. dtName : theList:=fNamedEntries;
  295. end;
  296. if InternalFind(theList,aType,idx) then
  297. subitem:=TResourceTreeNode(theList[idx])
  298. else exit;
  299. Result:=subitem.FindFreeID(aType);
  300. end;
  301. function TRootResTreeNode.InternalFind(aType, aName : TResourceDesc;
  302. const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource;
  303. var theList : TFPList;
  304. idx : integer;
  305. subitem : TResourceTreeNode;
  306. begin
  307. Result:=nil;
  308. case aType.DescType of
  309. dtID : theList:=fIDEntries;
  310. dtName : theList:=fNamedEntries;
  311. end;
  312. if InternalFind(theList,aType,idx) then
  313. subitem:=TResourceTreeNode(theList[idx])
  314. else exit;
  315. Result:=subitem.InternalFind(aType,aName,aLangID,noLangID,toDelete);
  316. if toDelete and ((subitem.IDCount+subitem.NamedCount)=0) then
  317. begin
  318. subitem.Free;
  319. theList.Delete(idx);
  320. end;
  321. end;
  322. { TTypeResTreeNode }
  323. constructor TTypeResTreeNode.Create(aType: TResourceDesc;
  324. aParent: TResourceTreeNode);
  325. begin
  326. inherited Create;
  327. fDesc.Assign(aType);
  328. fParent:=aParent;
  329. end;
  330. function TTypeResTreeNode.CreateSubNode(aDesc: TResourceDesc
  331. ): TResourceTreeNode;
  332. var theList : TFPList;
  333. begin
  334. case aDesc.DescType of
  335. dtID : theList:=fIDEntries;
  336. dtName : theList:=fNamedEntries;
  337. end;
  338. Result:=TNameResTreeNode.Create(aDesc,self);
  339. thelist.Add(Result);
  340. end;
  341. procedure TTypeResTreeNode.Add(aResource: TAbstractResource);
  342. var theList : TFPList;
  343. idx : integer;
  344. subitem : TResourceTreeNode;
  345. begin
  346. case aResource.Name.DescType of
  347. dtID : theList:=fIDEntries;
  348. dtName : theList:=fNamedEntries;
  349. end;
  350. if InternalFind(theList,aResource.Name,idx) then
  351. subitem:=TResourceTreeNode(theList[idx])
  352. else
  353. begin
  354. subitem:=TNameResTreeNode.Create(aResource.Name,self);
  355. theList.Insert(idx,subitem);
  356. end;
  357. subitem.Add(aResource);
  358. end;
  359. function TTypeResTreeNode.FindFreeID(aType: TResourceDesc): TResID;
  360. var i : integer;
  361. begin
  362. Result:=1;
  363. if IDCount<=0 then exit; //no items, use 1
  364. Result:=IDEntries[IDCount-1].Desc.ID+1; //try last one+1
  365. if Result>$FFFF then
  366. if IDEntries[0].Desc.ID>1 then Result:=IDEntries[0].Desc.ID-1 //try first one-1
  367. else
  368. begin //scan the whole list to find the first free id.
  369. Result:=1;
  370. for i:=0 to IDCount-1 do
  371. begin
  372. if IDEntries[i].Desc.ID<>Result then exit;
  373. inc(Result);
  374. end;
  375. raise ENoMoreFreeIDsException.Create('');
  376. end;
  377. end;
  378. function TTypeResTreeNode.InternalFind(aType, aName : TResourceDesc;
  379. const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource;
  380. var theList : TFPList;
  381. idx : integer;
  382. subitem : TResourceTreeNode;
  383. begin
  384. Result:=nil;
  385. case aName.DescType of
  386. dtID : theList:=fIDEntries;
  387. dtName : theList:=fNamedEntries;
  388. end;
  389. if InternalFind(theList,aName,idx) then
  390. subitem:=TResourceTreeNode(theList[idx])
  391. else exit;
  392. Result:=subitem.InternalFind(aType,aName,aLangID,noLangID,toDelete);
  393. if toDelete and ((subitem.IDCount+subitem.NamedCount)=0) then
  394. begin
  395. subitem.Free;
  396. theList.Delete(idx);
  397. end;
  398. end;
  399. { TNameResTreeNode }
  400. constructor TNameResTreeNode.Create(aName: TResourceDesc;
  401. aParent: TResourceTreeNode);
  402. begin
  403. inherited Create;
  404. fDesc.Assign(aName);
  405. fParent:=aParent;
  406. end;
  407. function TNameResTreeNode.CreateSubNode(aDesc: TResourceDesc
  408. ): TResourceTreeNode;
  409. var theList : TFPList;
  410. begin
  411. case aDesc.DescType of
  412. dtID : theList:=fIDEntries;
  413. dtName : theList:=fNamedEntries;
  414. end;
  415. Result:=TLangIDResTreeNode.Create(aDesc.ID,nil,self);
  416. thelist.Add(Result);
  417. end;
  418. procedure TNameResTreeNode.Add(aResource: TAbstractResource);
  419. var idx : integer;
  420. subitem : TResourceTreeNode;
  421. begin
  422. if InternalFind(fIDEntries,aResource.LangID,idx) then
  423. raise EResourceDuplicateException.Create('');
  424. subitem:=TLangIDResTreeNode.Create(aResource.LangID,aResource,self);
  425. fIDEntries.Insert(idx,subitem);
  426. end;
  427. function TNameResTreeNode.InternalFind(aType, aName : TResourceDesc;
  428. const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource;
  429. var idx : integer;
  430. begin
  431. Result:=nil;
  432. if noLangID then
  433. begin
  434. if IDCount<=0 then exit
  435. else idx:=0;
  436. end
  437. else
  438. if not InternalFind(fIDEntries,aLangID,idx) then exit;
  439. Result:=IDEntries[idx].Data;
  440. if toDelete then
  441. begin
  442. IDEntries[idx].Free;
  443. fIDEntries.Delete(idx);
  444. end;
  445. end;
  446. { TLangIDResTreeNode }
  447. function TLangIDResTreeNode.GetData: TAbstractResource;
  448. begin
  449. Result:=fData;
  450. end;
  451. constructor TLangIDResTreeNode.Create(aLangID: TLangID;
  452. aResource: TAbstractResource; aParent: TResourceTreeNode);
  453. begin
  454. inherited Create;
  455. fDesc.ID:=aLangID;
  456. fData:=aResource;
  457. fParent:=aParent;
  458. end;
  459. function TLangIDResTreeNode.CreateResource: TAbstractResource;
  460. var theType, theName : TResourceDesc;
  461. begin
  462. Result:=nil;
  463. if fData<>nil then exit;
  464. theType:=Parent.Parent.Desc;
  465. theName:=Parent.Desc;
  466. fData:=TResourceFactory.CreateResource(theType,theName);
  467. fData.LangID:=fDesc.ID;
  468. Result:=fData;
  469. end;
  470. function TLangIDResTreeNode.CreateSubNode(aDesc: TResourceDesc
  471. ): TResourceTreeNode;
  472. begin
  473. Result:=nil;
  474. end;
  475. procedure TLangIDResTreeNode.Add(aResource: TAbstractResource);
  476. begin
  477. //can't add, it's a leaf node
  478. end;
  479. function TLangIDResTreeNode.InternalFind(aType, aName : TResourceDesc;
  480. const aLangID : TLangID; const noLangID,toDelete : boolean) : TAbstractResource;
  481. begin
  482. //can't find, it's a leaf node
  483. Result:=nil;
  484. end;
  485. function TLangIDResTreeNode.IsLeaf: boolean;
  486. begin
  487. Result:=true;
  488. end;
  489. end.