wresourc.pas 21 KB

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