wresourc.pas 22 KB

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