wresourc.pas 21 KB

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