2
0

wresourc.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797
  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,get_caller_frame(get_frame),@Self,EP)))<>0 then
  202. begin
  203. P := EP;
  204. Break;
  205. end;
  206. end;
  207. FirstThatEntry:=P;
  208. end;
  209. procedure TResource.ForEachEntry(Func: pointer);
  210. var RP: PResourceEntry;
  211. I: sw_integer;
  212. begin
  213. for I:=0 to Items^.Count-1 do
  214. begin
  215. RP:=Items^.At(I);
  216. CallPointerMethodLocal(Func,get_caller_frame(get_frame),@Self,RP);
  217. end;
  218. end;
  219. procedure TResource.BuildHeader(var Header : TResourceHeader);
  220. begin
  221. FillChar(Header,SizeOf(Header),0);
  222. Header._Class:=_Class;
  223. Header.Flags:=Flags;
  224. Header.NameLen:=length(GetName);
  225. Header.EntryCount:=Items^.Count;
  226. end;
  227. destructor TResource.Done;
  228. begin
  229. inherited Done;
  230. if Name<>nil then DisposeStr(Name); Name:=nil;
  231. if Items<>nil then Dispose(Items, Done); Items:=nil;
  232. end;
  233. function TResourceCollection.At(Index: Sw_Integer): PResource;
  234. begin
  235. At:=inherited At(Index);
  236. end;
  237. function TResourceCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
  238. var K1: PResource absolute Key1;
  239. K2: PResource absolute Key2;
  240. N1,N2: string;
  241. Re: Sw_integer;
  242. begin
  243. N1:=UpcaseStr(K1^.GetName); N2:=UpcaseStr(K2^.GetName);
  244. if N1<N2 then Re:=-1 else
  245. if N1>N2 then Re:= 1 else
  246. Re:=0;
  247. Compare:=Re;
  248. end;
  249. function TResourceCollection.SearchResourceByName(const AName: string): PResource;
  250. var P,R: PResource;
  251. Index: sw_integer;
  252. begin
  253. New(R, Init(AName,0,0));
  254. if Search(R,Index)=false then P:=nil else
  255. P:=At(Index);
  256. Dispose(R, Done);
  257. SearchResourceByName:=P;
  258. end;
  259. constructor TResourceFile.Create(var RS: TStream);
  260. begin
  261. if Init(RS,false)=false then
  262. Fail;
  263. end;
  264. constructor TResourceFile.Load(var RS: TStream);
  265. begin
  266. if Init(RS,true)=false then
  267. Fail;
  268. end;
  269. constructor TResourceFile.Init(var RS: TStream; ALoad: boolean);
  270. var OK: boolean;
  271. RH: TResourceHeader;
  272. REH: TResourceEntryHeader;
  273. EndPos,I: longint;
  274. P: PResource;
  275. E: PResourceEntry;
  276. St: string;
  277. begin
  278. inherited Init;
  279. S:=@RS;
  280. New(Resources, Init(100, 1000));
  281. New(Entries, Init(500,2000));
  282. OK:=true;
  283. if ALoad=false then
  284. Modified:=true
  285. else
  286. begin
  287. S^.Reset;
  288. BaseOfs:=S^.GetPos;
  289. S^.Read(Header,SizeOf(Header));
  290. OK:=(S^.Status=stOK) and
  291. (Header.Signature=TPDataBlockSignature) and
  292. (Header.InfoType=ResourceBlockSignature);
  293. if OK then begin S^.Seek(BaseOfs+Header.TableOfs); OK:=S^.Status=stOK; end;
  294. EndPos:=BaseOfs+Header.InfoSize;
  295. if OK then
  296. while OK and (S^.GetPos<EndPos) do
  297. begin
  298. S^.Read(RH,SizeOf(RH)); OK:=(S^.Status=stOK);
  299. if OK then begin St[0]:=chr(RH.NameLen); S^.Read(St[1],RH.NameLen); OK:=(S^.Status=stOK); end;
  300. if OK then
  301. begin
  302. New(P, Init(St,RH._Class,RH.Flags));
  303. Resources^.Insert(P);
  304. end;
  305. I:=0;
  306. while OK and (I<RH.EntryCount) do
  307. begin
  308. S^.Read(REH,SizeOf(REH)); OK:=(S^.Status=stOK);
  309. if OK then
  310. begin
  311. New(E, Init(REH.ID,REH.LangID,REH.Flags,REH.DataLen));
  312. AddResEntryPtr(P,E);
  313. end;
  314. if OK then Inc(I);
  315. end;
  316. if OK then UpdateBlockDatas;
  317. end;
  318. end;
  319. if OK=false then
  320. begin
  321. Done;
  322. Fail;
  323. end;
  324. end;
  325. function TResourceFile.FirstThatResource(Func: pointer): PResource;
  326. var RP,P: PResource;
  327. I: sw_integer;
  328. begin
  329. P:=nil;
  330. for I:=0 to Resources^.Count-1 do
  331. begin
  332. RP:=Resources^.At(I);
  333. if Byte(Longint(CallPointerMethodLocal(Func,get_caller_frame(get_frame),@Self,RP)))<>0 then
  334. begin
  335. P := RP;
  336. Break;
  337. end;
  338. end;
  339. FirstThatResource:=P;
  340. end;
  341. procedure TResourceFile.ForEachResource(Func: pointer);
  342. var RP: PResource;
  343. I: sw_integer;
  344. begin
  345. for I:=0 to Resources^.Count-1 do
  346. begin
  347. RP:=Resources^.At(I);
  348. CallPointerMethodLocal(Func,get_caller_frame(get_frame),@Self,RP);
  349. end;
  350. end;
  351. procedure TResourceFile.ForEachResourceEntry(Func: pointer);
  352. var E: PResourceEntry;
  353. I: sw_integer;
  354. begin
  355. for I:=0 to Entries^.Count-1 do
  356. begin
  357. E:=Entries^.At(I);
  358. CallPointerMethodLocal(Func,get_caller_frame(get_frame),@Self,E);
  359. end;
  360. end;
  361. function TResourceFile.CreateResource(const Name: string; AClass, AFlags: longint): boolean;
  362. var OK: boolean;
  363. P: PResource;
  364. begin
  365. OK:=FindResource(Name)=nil;
  366. if OK then
  367. begin
  368. New(P, Init(Name,AClass,AFlags));
  369. Resources^.Insert(P);
  370. Modified:=true;
  371. end;
  372. CreateResource:=OK;
  373. end;
  374. function TResourceFile.AddResourceEntry(const ResName: string; ALangID, AFlags: longint; var Data;
  375. ADataSize: sw_integer): boolean;
  376. const BlockSize = 4096;
  377. var OK: boolean;
  378. P: PResource;
  379. E: PResourceEntry;
  380. RemSize,CurOfs,FragSize: longint;
  381. begin
  382. P:=FindResource(ResName);
  383. OK:=P<>nil;
  384. if OK then
  385. OK:=(P^.Items^.SearchEntryForLang(ALangID)=nil);
  386. if OK then
  387. begin
  388. New(E, Init(GetNextEntryID,ALangID, AFlags, ADataSize));
  389. AddResEntryPtr(P,E);
  390. UpdateBlockDatas;
  391. RemSize:=ADataSize; CurOfs:=0;
  392. S^.Reset;
  393. S^.Seek(BaseOfs+E^.DataOfs);
  394. while (RemSize>0) do
  395. begin
  396. FragSize:=Min(RemSize,BlockSize);
  397. S^.Write(PByteArray(@Data)^[CurOfs],FragSize);
  398. Dec(RemSize,FragSize); Inc(CurOfs,FragSize);
  399. end;
  400. Modified:=true;
  401. end;
  402. AddResourceEntry:=OK;
  403. end;
  404. function TResourceFile.AddResourceEntryFromStream(const ResName: string; ALangID, AFlags: longint;
  405. var Source: TStream; ADataSize: longint): boolean;
  406. const BufSize = 4096;
  407. var OK: boolean;
  408. P: PResource;
  409. E: PResourceEntry;
  410. RemSize,FragSize: longint;
  411. Buf: pointer;
  412. begin
  413. P:=FindResource(ResName);
  414. OK:=P<>nil;
  415. if OK then
  416. OK:=(P^.Items^.SearchEntryForLang(ALangID)=nil);
  417. if OK then
  418. begin
  419. New(E, Init(GetNextEntryID, ALangID, AFlags, ADataSize));
  420. AddResEntryPtr(P,E);
  421. UpdateBlockDatas;
  422. GetMem(Buf,BufSize);
  423. RemSize:=ADataSize;
  424. S^.Reset;
  425. S^.Seek(BaseOfs+E^.DataOfs);
  426. while (RemSize>0) do
  427. begin
  428. FragSize:=Min(RemSize,BufSize);
  429. Source.Read(Buf^,FragSize);
  430. S^.Write(Buf^,FragSize);
  431. Dec(RemSize,FragSize);
  432. end;
  433. FreeMem(Buf,BufSize);
  434. Modified:=true;
  435. end;
  436. AddResourceEntryFromStream:=OK;
  437. end;
  438. function TResourceFile.DeleteResourceEntry(const ResName: string; ALangID: longint): boolean;
  439. var E: PResourceEntry;
  440. P: PResource;
  441. OK: boolean;
  442. begin
  443. P:=FindResource(ResName);
  444. OK:=P<>nil;
  445. if OK then E:=P^.Items^.SearchEntryForLang(ALangID);
  446. OK:=OK and (E<>nil);
  447. if OK then
  448. begin
  449. OK:=DeleteArea(E^.DataOfs,E^.DataLen,GetTotalSize(false));
  450. if OK then begin RemoveResEntryPtr(P,E); Dispose(E, Done); end;
  451. Modified:=true;
  452. end;
  453. DeleteResourceEntry:=OK;
  454. end;
  455. function TResourceFile.DeleteResource(const ResName: string): boolean;
  456. var P: PResource;
  457. E: PResourceEntry;
  458. OK: boolean;
  459. begin
  460. P:=FindResource(ResName);
  461. OK:=P<>nil;
  462. if P<>nil then
  463. begin
  464. while OK and (P^.Items^.Count>0) do
  465. begin
  466. E:=P^.Items^.At(P^.Items^.Count-1);
  467. OK:=OK and DeleteResourceEntry(ResName,E^.LangID);
  468. end;
  469. Modified:=true;
  470. end;
  471. if OK then Resources^.Free(P);
  472. DeleteResource:=OK;
  473. end;
  474. function TResourceFile.ReadResourceEntry(const ResName: string; ALangID: longint; var Buf; BufSize: sw_word): boolean;
  475. var E: PResourceEntry;
  476. P: PResource;
  477. OK: boolean;
  478. CurOfs,CurFrag: sw_word;
  479. TempBuf: pointer;
  480. const TempBufSize = 4096;
  481. begin
  482. E:=nil;
  483. P:=FindResource(ResName);
  484. OK:=P<>nil;
  485. if OK then E:=P^.Items^.SearchEntryForLang(ALangID);
  486. OK:=OK and (E<>nil);
  487. OK:=OK and (E^.DataLen<=BufSize);
  488. if OK then
  489. begin
  490. GetMem(TempBuf,TempBufSize);
  491. S^.Reset;
  492. S^.Seek(BaseOfs+E^.DataOfs);
  493. OK:=(S^.Status=stOK);
  494. CurOfs:=0;
  495. while OK and (CurOfs<E^.DataLen) do
  496. begin
  497. CurFrag:=Min(E^.DataLen-CurOfs,TempBufSize);
  498. S^.Read(TempBuf^,CurFrag);
  499. OK:=OK and (S^.Status=stOK);
  500. if OK then
  501. Move(TempBuf^,PByteArray(@Buf)^[CurOfs],CurFrag);
  502. Inc(CurOfs,CurFrag);
  503. end;
  504. FreeMem(TempBuf,TempBufSize);
  505. end;
  506. ReadResourceEntry:=OK;
  507. end;
  508. function TResourceFile.ReadResourceEntryToStream(const ResName: string; ALangID: longint; var DestS: TStream): boolean;
  509. var E: PResourceEntry;
  510. P: PResource;
  511. OK: boolean;
  512. CurOfs,CurFrag: sw_word;
  513. TempBuf: pointer;
  514. const TempBufSize = 4096;
  515. begin
  516. P:=FindResource(ResName);
  517. OK:=P<>nil;
  518. if OK then E:=P^.Items^.SearchEntryForLang(ALangID);
  519. OK:=OK and (E<>nil);
  520. if OK then
  521. begin
  522. GetMem(TempBuf,TempBufSize);
  523. S^.Reset;
  524. S^.Seek(BaseOfs+E^.DataOfs);
  525. OK:=(S^.Status=stOK);
  526. CurOfs:=0;
  527. { this results sometimes in endless loops
  528. when the resource are changed PM }
  529. if E^.DataLen<0 then
  530. OK:=false;
  531. while OK and (CurOfs<E^.DataLen) do
  532. begin
  533. CurFrag:=Min(E^.DataLen-CurOfs,TempBufSize);
  534. S^.Read(TempBuf^,CurFrag);
  535. OK:=OK and (S^.Status=stOK);
  536. if OK then
  537. DestS.Write(TempBuf^,CurFrag);
  538. OK:=OK and (DestS.Status=stOK);
  539. Inc(CurOfs,CurFrag);
  540. end;
  541. FreeMem(TempBuf,TempBufSize);
  542. end;
  543. ReadResourceEntryToStream:=OK;
  544. end;
  545. function TResourceFile.FindResource(const ResName: string): PResource;
  546. begin
  547. FindResource:=Resources^.SearchResourceByName(ResName);
  548. end;
  549. function TResourceFile.FindResourceEntry(const ResName: string; ALangID: longint): PResourceEntry;
  550. var P: PResource;
  551. E: PResourceEntry;
  552. begin
  553. E:=nil;
  554. P:=FindResource(ResName);
  555. if P<>nil then
  556. E:=P^.Items^.SearchEntryForLang(ALangID);
  557. FindResourceEntry:=E;
  558. end;
  559. procedure TResourceFile.Flush;
  560. begin
  561. if Modified=false then Exit;
  562. BuildFileHeader;
  563. S^.Seek(BaseOfs);
  564. WriteHeader;
  565. S^.Seek(BaseOfs+Header.TableOfs);
  566. WriteResourceTable;
  567. S^.Truncate;
  568. Modified:=false;
  569. end;
  570. procedure TResourceFile.BuildFileHeader;
  571. begin
  572. FillChar(Header,SizeOf(Header),0);
  573. with Header do
  574. begin
  575. Signature:=TPDataBlockSignature;
  576. InfoType:=ResourceBlockSignature;
  577. InfoSize:=GetTotalSize(true);
  578. TableOfs:=GetTotalSize(false);
  579. end;
  580. end;
  581. procedure TResourceFile.WriteHeader;
  582. begin
  583. S^.Write(Header,SizeOf(Header));
  584. end;
  585. procedure TResourceFile.WriteResourceTable;
  586. var RH: TResourceHeader;
  587. REH: TResourceEntryHeader;
  588. procedure WriteResource(P: PResource);
  589. procedure WriteResourceEntry(P: PResourceEntry);
  590. begin
  591. P^.BuildHeader(REH);
  592. S^.Write(REH,SizeOf(REH));
  593. end;
  594. var N: string;
  595. begin
  596. if P^.Items^.Count=0 then Exit; { do not store resources with no entries }
  597. P^.BuildHeader(RH);
  598. S^.Write(RH,SizeOf(RH));
  599. N:=P^.GetName;
  600. S^.Write(N[1],length(N));
  601. P^.ForEachEntry(@WriteResourceEntry);
  602. end;
  603. begin
  604. ForEachResource(@WriteResource);
  605. end;
  606. procedure TResourceFile.UpdateBlockDatas;
  607. begin
  608. CalcSizes(false,true);
  609. end;
  610. function TResourceFile.GetTotalSize(IncludeHeaders: boolean): longint;
  611. begin
  612. GetTotalSize:=CalcSizes(IncludeHeaders,false);
  613. end;
  614. function TResourceFile.CalcSizes(IncludeHeaders, UpdatePosData: boolean): longint;
  615. var RH : TResourceHeader;
  616. REH : TResourceEntryHeader;
  617. Size: longint;
  618. NamesSize: longint;
  619. procedure AddResourceEntrySize(P: PResourceEntry);
  620. begin
  621. if UpdatePosData then P^.DataOfs:=Size;
  622. P^.BuildHeader(REH);
  623. Inc(Size,REH.DataLen);
  624. end;
  625. procedure AddResourceSize(P: PResource);
  626. var RH: TResourceHeader;
  627. begin
  628. P^.BuildHeader(RH);
  629. Inc(NamesSize,RH.NameLen);
  630. end;
  631. begin
  632. Size:=0; NamesSize:=0;
  633. Inc(Size,SizeOf(Header)); { this is on start so we always include it }
  634. ForEachResourceEntry(@AddResourceEntrySize);
  635. if IncludeHeaders then
  636. begin
  637. ForEachResource(@AddResourceSize);
  638. Inc(Size,SizeOf(RH)*Resources^.Count);
  639. Inc(Size,SizeOf(REH)*Entries^.Count);
  640. Inc(Size,NamesSize);
  641. end;
  642. CalcSizes:=Size;
  643. end;
  644. function TResourceFile.DeleteArea(AreaStart, AreaSize, TotalSize: longint): boolean;
  645. const BufSize = 4096;
  646. var RemSize,FragSize,CurOfs: longint;
  647. Buf: pointer;
  648. OK: boolean;
  649. begin
  650. GetMem(Buf,BufSize);
  651. RemSize:=TotalSize-(AreaStart+AreaSize); CurOfs:=0;
  652. OK:=RemSize>=0;
  653. while (RemSize>0) do
  654. begin
  655. FragSize:=Min(RemSize,BufSize);
  656. S^.Seek(BaseOfs+AreaStart+AreaSize+CurOfs);
  657. S^.Read(Buf^,BufSize);
  658. OK:=OK and (S^.Status=stOK);
  659. if OK then
  660. begin
  661. S^.Seek(BaseOfs+AreaStart+CurOfs);
  662. S^.Write(Buf^,BufSize);
  663. OK:=OK and (S^.Status=stOK);
  664. end;
  665. Inc(CurOfs,FragSize); Dec(RemSize,FragSize);
  666. end;
  667. FreeMem(Buf,BufSize);
  668. DeleteArea:=OK;
  669. end;
  670. procedure TResourceFile.AddResEntryPtr(P: PResource; E: PResourceEntry);
  671. begin
  672. if (P=nil) or (E=nil) then Exit;
  673. P^.Items^.Insert(E);
  674. Entries^.Insert(E);
  675. end;
  676. procedure TResourceFile.RemoveResEntryPtr(P: PResource; E: PResourceEntry);
  677. begin
  678. if (P=nil) or (E=nil) then Exit;
  679. Entries^.Delete(E);
  680. P^.Items^.Delete(E);
  681. end;
  682. function TResourceFile.GetNextEntryID: longint;
  683. var ID: longint;
  684. begin
  685. if Entries^.Count=0 then ID:=1 else
  686. ID:=Entries^.At(Entries^.Count-1)^.ID+1;
  687. GetNextEntryID:=ID;
  688. end;
  689. destructor TResourceFile.Done;
  690. begin
  691. Flush;
  692. inherited Done;
  693. { if assigned(S) then dispose(S,Done); S:=nil;}
  694. if Resources<>nil then Dispose(Resources, Done); Resources:=nil;
  695. if Entries<>nil then
  696. begin Entries^.DeleteAll; Dispose(Entries, Done); Entries:=nil; end;
  697. if MyStream and Assigned(S) then
  698. Dispose(S, Done);
  699. end;
  700. constructor TResourceFile.CreateFile(AFileName: string);
  701. var B: PFastBufStream;
  702. begin
  703. New(B, Init(AFileName, stCreate, 4096));
  704. if (B<>nil) and (B^.Status<>stOK) then
  705. begin Dispose(B, Done); B:=nil; end;
  706. if B=nil then Fail;
  707. if Create(B^)=false then
  708. Begin
  709. Dispose(B,Done);
  710. Fail;
  711. End;
  712. MyStream:=true;
  713. end;
  714. constructor TResourceFile.LoadFile(AFileName: string);
  715. var B: PFastBufStream;
  716. begin
  717. New(B, Init(AFileName, stOpen, 4096));
  718. if (B<>nil) and (B^.Status<>stOK) then
  719. begin Dispose(B, Done); B:=nil; end;
  720. if B=nil then Fail;
  721. if Load(B^)=false then
  722. Begin
  723. Dispose(B,Done);
  724. Fail;
  725. End;
  726. MyStream:=true;
  727. end;
  728. END.