wresourc.pas 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808
  1. {
  2. This file is part of the Free Pascal Integrated Development Environment
  3. Copyright (c) 1998 by Berczi Gabor
  4. Resource File support objects and routines
  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 WResourc;
  12. {$ifdef cpullvm}
  13. {$modeswitch nestedprocvars}
  14. {$endif}
  15. {$H-}
  16. interface
  17. uses Objects;
  18. const
  19. TPDataBlockSignature = ord('F')+ord('B')*256;
  20. ResourceBlockSignature = ord('R')+ord('D')*256;
  21. langDefault = 0;
  22. rcBinary = 1;
  23. type
  24. TResourceEntryHeader = packed record
  25. ID : longint;
  26. LangID : longint;
  27. Flags : longint;
  28. DataOfs: longint;
  29. DataLen: sw_word;
  30. end;
  31. TResourceHeader = packed record
  32. _Class : longint;
  33. Flags : longint;
  34. NameLen : word;
  35. EntryCount : word;
  36. end;
  37. TResourceFileHeader = packed record
  38. Signature : word;
  39. InfoType : word;
  40. InfoSize : longint;
  41. { ---- }
  42. TableOfs : longint;
  43. end;
  44. PResourceEntry = ^TResourceEntry;
  45. TResourceEntry = object(TObject)
  46. constructor Init(AID, ALangID, AFlags, ADataLen: longint);
  47. private
  48. ID : longint;
  49. LangID : longint;
  50. Flags : longint;
  51. DataOfs : longint;
  52. DataLen : sw_word;
  53. procedure BuildHeader(var Header : TResourceEntryHeader);
  54. end;
  55. PResourceEntryCollection = ^TResourceEntryCollection;
  56. TResourceEntryCollection = object(TSortedCollection)
  57. function At(Index: Sw_Integer): PResourceEntry;
  58. function Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
  59. function SearchEntryForLang(ALangID: longint): PResourceEntry;
  60. end;
  61. PGlobalResourceEntryCollection = ^TGlobalResourceEntryCollection;
  62. TGlobalResourceEntryCollection = object(TSortedCollection)
  63. function At(Index: Sw_Integer): PResourceEntry;
  64. function Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
  65. end;
  66. PResource = ^TResource;
  67. TResource = object(TObject)
  68. constructor Init(const AName: string; AClass, AFlags: longint);
  69. function GetName: string; virtual;
  70. function FirstThatEntry(Func: TCallbackFunBoolParam): PResourceEntry; virtual;
  71. procedure ForEachEntry(Func: TCallbackProcParam); virtual;
  72. destructor Done; virtual;
  73. private
  74. Name : PString;
  75. _Class : longint;
  76. Flags : longint;
  77. Items : PResourceEntryCollection;
  78. procedure BuildHeader(var Header : TResourceHeader);
  79. end;
  80. TResourceCollection = object(TSortedCollection)
  81. function At(Index: Sw_Integer): PResource;
  82. function Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
  83. function SearchResourceByName(const AName: string): PResource;
  84. end;
  85. PResourceCollection = ^TResourceCollection;
  86. TResourceFile = object(TObject)
  87. constructor Init(var RS: TStream; ALoad: boolean);
  88. constructor Create(var RS: TStream);
  89. constructor Load(var RS: TStream);
  90. constructor CreateFile(AFileName: string);
  91. constructor LoadFile(AFileName: string);
  92. function FirstThatResource(Func: TCallbackFunBoolParam): PResource; virtual;
  93. procedure ForEachResource(Func: TCallbackProcParam); virtual;
  94. procedure ForEachResourceEntry(Func: TCallbackProcParam); virtual;
  95. function CreateResource(const Name: string; AClass, AFlags: longint): boolean; virtual;
  96. function AddResourceEntry(const ResName: string; ALangID, AFlags: longint; var Data;
  97. ADataSize: sw_integer): boolean; virtual;
  98. function AddResourceEntryFromStream(const ResName: string; ALangID, AFlags: longint;
  99. var Source: TStream; ADataSize: longint): boolean; virtual;
  100. function DeleteResourceEntry(const ResName: string; ALangID: longint): boolean; virtual;
  101. function DeleteResource(const ResName: string): boolean; virtual;
  102. function ReadResourceEntry(const ResName: string; ALangID: longint; var Buf; BufSize: sw_word): boolean;
  103. function ReadResourceEntryToStream(const ResName: string; ALangID: longint; var DestS: TStream): boolean;
  104. procedure Flush; virtual;
  105. destructor Done; virtual;
  106. public
  107. BaseOfs: longint;
  108. function FindResource(const ResName: string): PResource;
  109. function FindResourceEntry(const ResName: string; ALangID: longint): PResourceEntry;
  110. private
  111. S : PStream;
  112. MyStream : boolean;
  113. Resources : PResourceCollection;
  114. Entries : PGlobalResourceEntryCollection;
  115. Header : TResourceFileHeader;
  116. Modified : boolean;
  117. procedure UpdateBlockDatas;
  118. function GetNextEntryID: longint;
  119. function GetTotalSize(IncludeHeaders: boolean): longint;
  120. function CalcSizes(IncludeHeaders, UpdatePosData: boolean): longint;
  121. procedure AddResEntryPtr(P: PResource; E: PResourceEntry);
  122. procedure RemoveResEntryPtr(P: PResource; E: PResourceEntry);
  123. function DeleteArea(AreaStart, AreaSize, TotalSize: longint): boolean;
  124. procedure BuildFileHeader;
  125. procedure WriteHeader;
  126. procedure WriteResourceTable;
  127. end;
  128. PResourceFile = ^TResourceFile;
  129. implementation
  130. uses
  131. WUtils;
  132. function TResourceEntryCollection.At(Index: Sw_Integer): PResourceEntry;
  133. begin
  134. At:=inherited At(Index);
  135. end;
  136. function TResourceEntryCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
  137. var K1: PResourceEntry absolute Key1;
  138. K2: PResourceEntry absolute Key2;
  139. Re: Sw_integer;
  140. begin
  141. if K1^.LangID<K2^.LangID then Re:=-1 else
  142. if K1^.LangID>K2^.LangID then Re:= 1 else
  143. Re:=0;
  144. Compare:=Re;
  145. end;
  146. function TResourceEntryCollection.SearchEntryForLang(ALangID: longint): PResourceEntry;
  147. var P: PResourceEntry;
  148. E: TResourceEntry;
  149. Index: sw_integer;
  150. begin
  151. E.LangID:=ALangID;
  152. if Search(@E,Index)=false then P:=nil else
  153. P:=At(Index);
  154. SearchEntryForLang:=P;
  155. end;
  156. function TGlobalResourceEntryCollection.At(Index: Sw_Integer): PResourceEntry;
  157. begin
  158. At:=inherited At(Index);
  159. end;
  160. function TGlobalResourceEntryCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
  161. var K1: PResourceEntry absolute Key1;
  162. K2: PResourceEntry absolute Key2;
  163. Re: Sw_integer;
  164. begin
  165. if K1^.ID<K2^.ID then Re:=-1 else
  166. if K1^.ID>K2^.ID then Re:= 1 else
  167. Re:=0;
  168. Compare:=Re;
  169. end;
  170. constructor TResourceEntry.Init(AID, ALangID, AFlags, ADataLen: longint);
  171. begin
  172. inherited Init;
  173. ID:=AID;
  174. LangID:=ALangID; Flags:=AFlags; DataLen:=ADataLen;
  175. end;
  176. procedure TResourceEntry.BuildHeader(var Header : TResourceEntryHeader);
  177. begin
  178. FillChar(Header,SizeOf(Header),0);
  179. Header.ID:=ID;
  180. Header.LangID:=LangID;
  181. Header.Flags:=Flags;
  182. Header.DataLen:=DataLen;
  183. Header.DataOfs:=DataOfs;
  184. end;
  185. constructor TResource.Init(const AName: string; AClass, AFlags: longint);
  186. begin
  187. inherited Init;
  188. Name:=NewStr(AName);
  189. _Class:=AClass;
  190. Flags:=AFlags;
  191. New(Items, Init(10,50));
  192. end;
  193. function TResource.GetName: string;
  194. begin
  195. GetName:=GetStr(Name);
  196. end;
  197. function TResource.FirstThatEntry(Func: TCallbackFunBoolParam): PResourceEntry;
  198. var EP,P: PResourceEntry;
  199. I: sw_integer;
  200. begin
  201. P:=nil;
  202. for I:=0 to Items^.Count-1 do
  203. begin
  204. EP:=Items^.At(I);
  205. if Byte(Longint(CallPointerMethodLocal(Func,
  206. get_caller_frame(get_frame,get_pc_addr),@Self,EP)))<>0 then
  207. begin
  208. P := EP;
  209. Break;
  210. end;
  211. end;
  212. FirstThatEntry:=P;
  213. end;
  214. procedure TResource.ForEachEntry(Func: TCallbackProcParam);
  215. var RP: PResourceEntry;
  216. I: sw_integer;
  217. begin
  218. for I:=0 to Items^.Count-1 do
  219. begin
  220. RP:=Items^.At(I);
  221. CallPointerMethodLocal(Func,
  222. get_caller_frame(get_frame,get_pc_addr),@Self,RP);
  223. end;
  224. end;
  225. procedure TResource.BuildHeader(var Header : TResourceHeader);
  226. begin
  227. FillChar(Header,SizeOf(Header),0);
  228. Header._Class:=_Class;
  229. Header.Flags:=Flags;
  230. Header.NameLen:=length(GetName);
  231. Header.EntryCount:=Items^.Count;
  232. end;
  233. destructor TResource.Done;
  234. begin
  235. inherited Done;
  236. if Name<>nil then DisposeStr(Name); Name:=nil;
  237. if Items<>nil then Dispose(Items, Done); Items:=nil;
  238. end;
  239. function TResourceCollection.At(Index: Sw_Integer): PResource;
  240. begin
  241. At:=inherited At(Index);
  242. end;
  243. function TResourceCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
  244. var K1: PResource absolute Key1;
  245. K2: PResource absolute Key2;
  246. N1,N2: string;
  247. Re: Sw_integer;
  248. begin
  249. N1:=UpcaseStr(K1^.GetName); N2:=UpcaseStr(K2^.GetName);
  250. if N1<N2 then Re:=-1 else
  251. if N1>N2 then Re:= 1 else
  252. Re:=0;
  253. Compare:=Re;
  254. end;
  255. function TResourceCollection.SearchResourceByName(const AName: string): PResource;
  256. var P,R: PResource;
  257. Index: sw_integer;
  258. begin
  259. New(R, Init(AName,0,0));
  260. if Search(R,Index)=false then P:=nil else
  261. P:=At(Index);
  262. Dispose(R, Done);
  263. SearchResourceByName:=P;
  264. end;
  265. constructor TResourceFile.Create(var RS: TStream);
  266. begin
  267. if Init(RS,false)=false then
  268. Fail;
  269. end;
  270. constructor TResourceFile.Load(var RS: TStream);
  271. begin
  272. if Init(RS,true)=false then
  273. Fail;
  274. end;
  275. constructor TResourceFile.Init(var RS: TStream; ALoad: boolean);
  276. var OK: boolean;
  277. RH: TResourceHeader;
  278. REH: TResourceEntryHeader;
  279. EndPos,I: longint;
  280. P: PResource;
  281. E: PResourceEntry;
  282. St: string;
  283. begin
  284. inherited Init;
  285. S:=@RS;
  286. New(Resources, Init(100, 1000));
  287. New(Entries, Init(500,2000));
  288. OK:=true;
  289. if ALoad=false then
  290. Modified:=true
  291. else
  292. begin
  293. S^.Reset;
  294. BaseOfs:=S^.GetPos;
  295. S^.Read(Header,SizeOf(Header));
  296. OK:=(S^.Status=stOK) and
  297. (Header.Signature=TPDataBlockSignature) and
  298. (Header.InfoType=ResourceBlockSignature);
  299. if OK then begin S^.Seek(BaseOfs+Header.TableOfs); OK:=S^.Status=stOK; end;
  300. EndPos:=BaseOfs+Header.InfoSize;
  301. if OK then
  302. while OK and (S^.GetPos<EndPos) do
  303. begin
  304. S^.Read(RH,SizeOf(RH)); OK:=(S^.Status=stOK);
  305. if OK then begin St[0]:=chr(RH.NameLen); S^.Read(St[1],RH.NameLen); OK:=(S^.Status=stOK); end;
  306. if OK then
  307. begin
  308. New(P, Init(St,RH._Class,RH.Flags));
  309. Resources^.Insert(P);
  310. end;
  311. I:=0;
  312. while OK and (I<RH.EntryCount) do
  313. begin
  314. S^.Read(REH,SizeOf(REH)); OK:=(S^.Status=stOK);
  315. if OK then
  316. begin
  317. New(E, Init(REH.ID,REH.LangID,REH.Flags,REH.DataLen));
  318. AddResEntryPtr(P,E);
  319. end;
  320. if OK then Inc(I);
  321. end;
  322. if OK then UpdateBlockDatas;
  323. end;
  324. end;
  325. if OK=false then
  326. begin
  327. Done;
  328. Fail;
  329. end;
  330. end;
  331. function TResourceFile.FirstThatResource(Func: TCallbackFunBoolParam): PResource;
  332. var RP,P: PResource;
  333. I: sw_integer;
  334. begin
  335. P:=nil;
  336. for I:=0 to Resources^.Count-1 do
  337. begin
  338. RP:=Resources^.At(I);
  339. if Byte(Longint(CallPointerMethodLocal(Func,
  340. get_caller_frame(get_frame,get_pc_addr),@Self,RP)))<>0 then
  341. begin
  342. P := RP;
  343. Break;
  344. end;
  345. end;
  346. FirstThatResource:=P;
  347. end;
  348. procedure TResourceFile.ForEachResource(Func: TCallbackProcParam);
  349. var RP: PResource;
  350. I: sw_integer;
  351. begin
  352. for I:=0 to Resources^.Count-1 do
  353. begin
  354. RP:=Resources^.At(I);
  355. CallPointerMethodLocal(Func,get_caller_frame(get_frame,get_pc_addr),@Self,RP);
  356. end;
  357. end;
  358. procedure TResourceFile.ForEachResourceEntry(Func: TCallbackProcParam);
  359. var E: PResourceEntry;
  360. I: sw_integer;
  361. begin
  362. for I:=0 to Entries^.Count-1 do
  363. begin
  364. E:=Entries^.At(I);
  365. CallPointerMethodLocal(Func,get_caller_frame(get_frame,get_pc_addr),@Self,E);
  366. end;
  367. end;
  368. function TResourceFile.CreateResource(const Name: string; AClass, AFlags: longint): boolean;
  369. var OK: boolean;
  370. P: PResource;
  371. begin
  372. OK:=FindResource(Name)=nil;
  373. if OK then
  374. begin
  375. New(P, Init(Name,AClass,AFlags));
  376. Resources^.Insert(P);
  377. Modified:=true;
  378. end;
  379. CreateResource:=OK;
  380. end;
  381. function TResourceFile.AddResourceEntry(const ResName: string; ALangID, AFlags: longint; var Data;
  382. ADataSize: sw_integer): boolean;
  383. const BlockSize = 4096;
  384. var OK: boolean;
  385. P: PResource;
  386. E: PResourceEntry;
  387. RemSize,CurOfs,FragSize: longint;
  388. begin
  389. P:=FindResource(ResName);
  390. OK:=P<>nil;
  391. if OK then
  392. OK:=(P^.Items^.SearchEntryForLang(ALangID)=nil);
  393. if OK then
  394. begin
  395. New(E, Init(GetNextEntryID,ALangID, AFlags, ADataSize));
  396. AddResEntryPtr(P,E);
  397. UpdateBlockDatas;
  398. RemSize:=ADataSize; CurOfs:=0;
  399. S^.Reset;
  400. S^.Seek(BaseOfs+E^.DataOfs);
  401. while (RemSize>0) do
  402. begin
  403. FragSize:=Min(RemSize,BlockSize);
  404. S^.Write(PByteArray(@Data)^[CurOfs],FragSize);
  405. Dec(RemSize,FragSize); Inc(CurOfs,FragSize);
  406. end;
  407. Modified:=true;
  408. end;
  409. AddResourceEntry:=OK;
  410. end;
  411. function TResourceFile.AddResourceEntryFromStream(const ResName: string; ALangID, AFlags: longint;
  412. var Source: TStream; ADataSize: longint): boolean;
  413. const BufSize = 4096;
  414. var OK: boolean;
  415. P: PResource;
  416. E: PResourceEntry;
  417. RemSize,FragSize: longint;
  418. Buf: pointer;
  419. begin
  420. P:=FindResource(ResName);
  421. OK:=P<>nil;
  422. if OK then
  423. OK:=(P^.Items^.SearchEntryForLang(ALangID)=nil);
  424. if OK then
  425. begin
  426. New(E, Init(GetNextEntryID, ALangID, AFlags, ADataSize));
  427. AddResEntryPtr(P,E);
  428. UpdateBlockDatas;
  429. GetMem(Buf,BufSize);
  430. RemSize:=ADataSize;
  431. S^.Reset;
  432. S^.Seek(BaseOfs+E^.DataOfs);
  433. while (RemSize>0) do
  434. begin
  435. FragSize:=Min(RemSize,BufSize);
  436. Source.Read(Buf^,FragSize);
  437. S^.Write(Buf^,FragSize);
  438. Dec(RemSize,FragSize);
  439. end;
  440. FreeMem(Buf,BufSize);
  441. Modified:=true;
  442. end;
  443. AddResourceEntryFromStream:=OK;
  444. end;
  445. function TResourceFile.DeleteResourceEntry(const ResName: string; ALangID: longint): boolean;
  446. var E: PResourceEntry;
  447. P: PResource;
  448. OK: boolean;
  449. begin
  450. P:=FindResource(ResName);
  451. OK:=P<>nil;
  452. if OK then E:=P^.Items^.SearchEntryForLang(ALangID);
  453. OK:=OK and (E<>nil);
  454. if OK then
  455. begin
  456. OK:=DeleteArea(E^.DataOfs,E^.DataLen,GetTotalSize(false));
  457. if OK then begin RemoveResEntryPtr(P,E); Dispose(E, Done); end;
  458. Modified:=true;
  459. end;
  460. DeleteResourceEntry:=OK;
  461. end;
  462. function TResourceFile.DeleteResource(const ResName: string): boolean;
  463. var P: PResource;
  464. E: PResourceEntry;
  465. OK: boolean;
  466. begin
  467. P:=FindResource(ResName);
  468. OK:=P<>nil;
  469. if P<>nil then
  470. begin
  471. while OK and (P^.Items^.Count>0) do
  472. begin
  473. E:=P^.Items^.At(P^.Items^.Count-1);
  474. OK:=OK and DeleteResourceEntry(ResName,E^.LangID);
  475. end;
  476. Modified:=true;
  477. end;
  478. if OK then Resources^.Free(P);
  479. DeleteResource:=OK;
  480. end;
  481. function TResourceFile.ReadResourceEntry(const ResName: string; ALangID: longint; var Buf; BufSize: sw_word): boolean;
  482. var E: PResourceEntry;
  483. P: PResource;
  484. OK: boolean;
  485. CurOfs,CurFrag: sw_word;
  486. TempBuf: pointer;
  487. const TempBufSize = 4096;
  488. begin
  489. E:=nil;
  490. P:=FindResource(ResName);
  491. OK:=P<>nil;
  492. if OK then E:=P^.Items^.SearchEntryForLang(ALangID);
  493. OK:=OK and (E<>nil);
  494. OK:=OK and (E^.DataLen<=BufSize);
  495. if OK then
  496. begin
  497. GetMem(TempBuf,TempBufSize);
  498. S^.Reset;
  499. S^.Seek(BaseOfs+E^.DataOfs);
  500. OK:=(S^.Status=stOK);
  501. CurOfs:=0;
  502. while OK and (CurOfs<E^.DataLen) do
  503. begin
  504. CurFrag:=Min(E^.DataLen-CurOfs,TempBufSize);
  505. S^.Read(TempBuf^,CurFrag);
  506. OK:=OK and (S^.Status=stOK);
  507. if OK then
  508. Move(TempBuf^,PByteArray(@Buf)^[CurOfs],CurFrag);
  509. Inc(CurOfs,CurFrag);
  510. end;
  511. FreeMem(TempBuf,TempBufSize);
  512. end;
  513. ReadResourceEntry:=OK;
  514. end;
  515. function TResourceFile.ReadResourceEntryToStream(const ResName: string; ALangID: longint; var DestS: TStream): boolean;
  516. var E: PResourceEntry;
  517. P: PResource;
  518. OK: boolean;
  519. CurOfs,CurFrag: sw_word;
  520. TempBuf: pointer;
  521. const TempBufSize = 4096;
  522. begin
  523. P:=FindResource(ResName);
  524. OK:=P<>nil;
  525. if OK then E:=P^.Items^.SearchEntryForLang(ALangID);
  526. OK:=OK and (E<>nil);
  527. if OK then
  528. begin
  529. GetMem(TempBuf,TempBufSize);
  530. S^.Reset;
  531. S^.Seek(BaseOfs+E^.DataOfs);
  532. OK:=(S^.Status=stOK);
  533. CurOfs:=0;
  534. { this results sometimes in endless loops
  535. when the resource are changed PM }
  536. if E^.DataLen<0 then
  537. OK:=false;
  538. while OK and (CurOfs<E^.DataLen) do
  539. begin
  540. CurFrag:=Min(E^.DataLen-CurOfs,TempBufSize);
  541. S^.Read(TempBuf^,CurFrag);
  542. OK:=OK and (S^.Status=stOK);
  543. if OK then
  544. DestS.Write(TempBuf^,CurFrag);
  545. OK:=OK and (DestS.Status=stOK);
  546. Inc(CurOfs,CurFrag);
  547. end;
  548. FreeMem(TempBuf,TempBufSize);
  549. end;
  550. ReadResourceEntryToStream:=OK;
  551. end;
  552. function TResourceFile.FindResource(const ResName: string): PResource;
  553. begin
  554. FindResource:=Resources^.SearchResourceByName(ResName);
  555. end;
  556. function TResourceFile.FindResourceEntry(const ResName: string; ALangID: longint): PResourceEntry;
  557. var P: PResource;
  558. E: PResourceEntry;
  559. begin
  560. E:=nil;
  561. P:=FindResource(ResName);
  562. if P<>nil then
  563. E:=P^.Items^.SearchEntryForLang(ALangID);
  564. FindResourceEntry:=E;
  565. end;
  566. procedure TResourceFile.Flush;
  567. begin
  568. if Modified=false then Exit;
  569. BuildFileHeader;
  570. S^.Seek(BaseOfs);
  571. WriteHeader;
  572. S^.Seek(BaseOfs+Header.TableOfs);
  573. WriteResourceTable;
  574. S^.Truncate;
  575. Modified:=false;
  576. end;
  577. procedure TResourceFile.BuildFileHeader;
  578. begin
  579. FillChar(Header,SizeOf(Header),0);
  580. with Header do
  581. begin
  582. Signature:=TPDataBlockSignature;
  583. InfoType:=ResourceBlockSignature;
  584. InfoSize:=GetTotalSize(true);
  585. TableOfs:=GetTotalSize(false);
  586. end;
  587. end;
  588. procedure TResourceFile.WriteHeader;
  589. begin
  590. S^.Write(Header,SizeOf(Header));
  591. end;
  592. procedure TResourceFile.WriteResourceTable;
  593. var RH: TResourceHeader;
  594. REH: TResourceEntryHeader;
  595. procedure WriteResource(P: PResource);
  596. procedure WriteResourceEntry(P: PResourceEntry);
  597. begin
  598. P^.BuildHeader(REH);
  599. S^.Write(REH,SizeOf(REH));
  600. end;
  601. var N: string;
  602. begin
  603. if P^.Items^.Count=0 then Exit; { do not store resources with no entries }
  604. P^.BuildHeader(RH);
  605. S^.Write(RH,SizeOf(RH));
  606. N:=P^.GetName;
  607. S^.Write(N[1],length(N));
  608. P^.ForEachEntry(TCallbackProcParam(@WriteResourceEntry));
  609. end;
  610. begin
  611. ForEachResource(TCallbackProcParam(@WriteResource));
  612. end;
  613. procedure TResourceFile.UpdateBlockDatas;
  614. begin
  615. CalcSizes(false,true);
  616. end;
  617. function TResourceFile.GetTotalSize(IncludeHeaders: boolean): longint;
  618. begin
  619. GetTotalSize:=CalcSizes(IncludeHeaders,false);
  620. end;
  621. function TResourceFile.CalcSizes(IncludeHeaders, UpdatePosData: boolean): longint;
  622. var RH : TResourceHeader;
  623. REH : TResourceEntryHeader;
  624. Size: longint;
  625. NamesSize: longint;
  626. procedure AddResourceEntrySize(P: PResourceEntry);
  627. begin
  628. if UpdatePosData then P^.DataOfs:=Size;
  629. P^.BuildHeader(REH);
  630. Inc(Size,REH.DataLen);
  631. end;
  632. procedure AddResourceSize(P: PResource);
  633. var RH: TResourceHeader;
  634. begin
  635. P^.BuildHeader(RH);
  636. Inc(NamesSize,RH.NameLen);
  637. end;
  638. begin
  639. Size:=0; NamesSize:=0;
  640. Inc(Size,SizeOf(Header)); { this is on start so we always include it }
  641. ForEachResourceEntry(TCallbackProcParam(@AddResourceEntrySize));
  642. if IncludeHeaders then
  643. begin
  644. ForEachResource(TCallbackProcParam(@AddResourceSize));
  645. Inc(Size,SizeOf(RH)*Resources^.Count);
  646. Inc(Size,SizeOf(REH)*Entries^.Count);
  647. Inc(Size,NamesSize);
  648. end;
  649. CalcSizes:=Size;
  650. end;
  651. function TResourceFile.DeleteArea(AreaStart, AreaSize, TotalSize: longint): boolean;
  652. const BufSize = 4096;
  653. var RemSize,FragSize,CurOfs: longint;
  654. Buf: pointer;
  655. OK: boolean;
  656. begin
  657. GetMem(Buf,BufSize);
  658. RemSize:=TotalSize-(AreaStart+AreaSize); CurOfs:=0;
  659. OK:=RemSize>=0;
  660. while (RemSize>0) do
  661. begin
  662. FragSize:=Min(RemSize,BufSize);
  663. S^.Seek(BaseOfs+AreaStart+AreaSize+CurOfs);
  664. S^.Read(Buf^,BufSize);
  665. OK:=OK and (S^.Status=stOK);
  666. if OK then
  667. begin
  668. S^.Seek(BaseOfs+AreaStart+CurOfs);
  669. S^.Write(Buf^,BufSize);
  670. OK:=OK and (S^.Status=stOK);
  671. end;
  672. Inc(CurOfs,FragSize); Dec(RemSize,FragSize);
  673. end;
  674. FreeMem(Buf,BufSize);
  675. DeleteArea:=OK;
  676. end;
  677. procedure TResourceFile.AddResEntryPtr(P: PResource; E: PResourceEntry);
  678. begin
  679. if (P=nil) or (E=nil) then Exit;
  680. P^.Items^.Insert(E);
  681. Entries^.Insert(E);
  682. end;
  683. procedure TResourceFile.RemoveResEntryPtr(P: PResource; E: PResourceEntry);
  684. begin
  685. if (P=nil) or (E=nil) then Exit;
  686. Entries^.Delete(E);
  687. P^.Items^.Delete(E);
  688. end;
  689. function TResourceFile.GetNextEntryID: longint;
  690. var ID: longint;
  691. begin
  692. if Entries^.Count=0 then ID:=1 else
  693. ID:=Entries^.At(Entries^.Count-1)^.ID+1;
  694. GetNextEntryID:=ID;
  695. end;
  696. destructor TResourceFile.Done;
  697. begin
  698. Flush;
  699. inherited Done;
  700. { if assigned(S) then dispose(S,Done); S:=nil;}
  701. if Resources<>nil then Dispose(Resources, Done); Resources:=nil;
  702. if Entries<>nil then
  703. begin Entries^.DeleteAll; Dispose(Entries, Done); Entries:=nil; end;
  704. if MyStream and Assigned(S) then
  705. Dispose(S, Done);
  706. end;
  707. constructor TResourceFile.CreateFile(AFileName: string);
  708. var B: PFastBufStream;
  709. begin
  710. New(B, Init(AFileName, stCreate, 4096));
  711. if (B<>nil) and (B^.Status<>stOK) then
  712. begin Dispose(B, Done); B:=nil; end;
  713. if B=nil then Fail;
  714. if Create(B^)=false then
  715. Begin
  716. Dispose(B,Done);
  717. Fail;
  718. End;
  719. MyStream:=true;
  720. {$ifdef HASAMIGA}
  721. Flush;
  722. {$endif}
  723. end;
  724. constructor TResourceFile.LoadFile(AFileName: string);
  725. var B: PFastBufStream;
  726. begin
  727. New(B, Init(AFileName, stOpen, 4096));
  728. if (B<>nil) and (B^.Status<>stOK) then
  729. begin Dispose(B, Done); B:=nil; end;
  730. if B=nil then Fail;
  731. if Load(B^)=false then
  732. Begin
  733. Dispose(B,Done);
  734. Fail;
  735. End;
  736. MyStream:=true;
  737. end;
  738. END.