wresourc.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833
  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 WResource;
  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: longint;
  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 : longint;
  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; var 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 CallSpec,
  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,PreviousFramePointer,@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,PreviousFramePointer,@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,PreviousFramePointer,@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,PreviousFramePointer,@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,PreviousFramePointer,@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; var 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. 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); {$ifndef FPC}far;{$endif}
  589. procedure WriteResourceEntry(P: PResourceEntry); {$ifndef FPC}far;{$endif}
  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. procedure AddResourceEntrySize(P: PResourceEntry); {$ifndef FPC}far;{$endif}
  619. begin
  620. if UpdatePosData then P^.DataOfs:=Size;
  621. P^.BuildHeader(REH);
  622. Inc(Size,REH.DataLen);
  623. end;
  624. begin
  625. Size:=0;
  626. Inc(Size,SizeOf(Header)); { this is on start so we always include it }
  627. ForEachResourceEntry(@AddResourceEntrySize);
  628. if IncludeHeaders then
  629. begin
  630. Inc(Size,SizeOf(RH)*Resources^.Count);
  631. Inc(Size,SizeOf(REH)*Entries^.Count);
  632. end;
  633. CalcSizes:=Size;
  634. end;
  635. function TResourceFile.DeleteArea(AreaStart, AreaSize, TotalSize: longint): boolean;
  636. const BufSize = 4096;
  637. var RemSize,FragSize,CurOfs: longint;
  638. Buf: pointer;
  639. OK: boolean;
  640. begin
  641. GetMem(Buf,BufSize);
  642. RemSize:=TotalSize-(AreaStart+AreaSize); CurOfs:=0;
  643. OK:=RemSize>=0;
  644. while (RemSize>0) do
  645. begin
  646. FragSize:=Min(RemSize,BufSize);
  647. S^.Seek(BaseOfs+AreaStart+AreaSize+CurOfs);
  648. S^.Read(Buf^,BufSize);
  649. OK:=OK and (S^.Status=stOK);
  650. if OK then
  651. begin
  652. S^.Seek(BaseOfs+AreaStart+CurOfs);
  653. S^.Write(Buf^,BufSize);
  654. OK:=OK and (S^.Status=stOK);
  655. end;
  656. Inc(CurOfs,FragSize); Dec(RemSize,FragSize);
  657. end;
  658. FreeMem(Buf,BufSize);
  659. DeleteArea:=OK;
  660. end;
  661. procedure TResourceFile.AddResEntryPtr(P: PResource; E: PResourceEntry);
  662. begin
  663. if (P=nil) or (E=nil) then Exit;
  664. P^.Items^.Insert(E);
  665. Entries^.Insert(E);
  666. end;
  667. procedure TResourceFile.RemoveResEntryPtr(P: PResource; E: PResourceEntry);
  668. begin
  669. if (P=nil) or (E=nil) then Exit;
  670. Entries^.Delete(E);
  671. P^.Items^.Delete(E);
  672. end;
  673. function TResourceFile.GetNextEntryID: longint;
  674. var ID: longint;
  675. begin
  676. if Entries^.Count=0 then ID:=1 else
  677. ID:=Entries^.At(Entries^.Count-1)^.ID+1;
  678. GetNextEntryID:=ID;
  679. end;
  680. destructor TResourceFile.Done;
  681. begin
  682. Flush;
  683. inherited Done;
  684. if assigned(S) then dispose(S,Done);
  685. S:=nil;
  686. if Resources<>nil then Dispose(Resources, Done); Resources:=nil;
  687. if Entries<>nil then
  688. begin Entries^.DeleteAll; Dispose(Entries, Done); Entries:=nil; end;
  689. if MyStream and Assigned(S) then
  690. Dispose(S, Done);
  691. end;
  692. constructor TResourceFile.CreateFile(AFileName: string);
  693. var B: PBufStream;
  694. begin
  695. New(B, Init(AFileName, stCreate, 4096));
  696. if (B<>nil) and (B^.Status<>stOK) then
  697. begin Dispose(B, Done); B:=nil; end;
  698. if B=nil then Fail;
  699. if Create(B^)=false then
  700. Fail;
  701. MyStream:=true;
  702. end;
  703. constructor TResourceFile.LoadFile(AFileName: string);
  704. var B: PBufStream;
  705. begin
  706. New(B, Init(AFileName, stOpen, 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 Load(B^)=false then
  711. Fail;
  712. MyStream:=true;
  713. end;
  714. END.
  715. {
  716. $Log$
  717. Revision 1.7 1999-09-07 09:26:26 pierre
  718. * E^.DataLen=-1 sets OK to false in TResourceFile.ReadSourceEntryToStream
  719. Revision 1.6 1999/08/03 20:22:44 peter
  720. + TTab acts now on Ctrl+Tab and Ctrl+Shift+Tab...
  721. + Desktop saving should work now
  722. - History saved
  723. - Clipboard content saved
  724. - Desktop saved
  725. - Symbol info saved
  726. * syntax-highlight bug fixed, which compared special keywords case sensitive
  727. (for ex. 'asm' caused asm-highlighting, while 'ASM' didn't)
  728. * with 'whole words only' set, the editor didn't found occourences of the
  729. searched text, if the text appeared previously in the same line, but didn't
  730. satisfied the 'whole-word' condition
  731. * ^QB jumped to (SelStart.X,SelEnd.X) instead of (SelStart.X,SelStart.Y)
  732. (ie. the beginning of the selection)
  733. * when started typing in a new line, but not at the start (X=0) of it,
  734. the editor inserted the text one character more to left as it should...
  735. * TCodeEditor.HideSelection (Ctrl-K+H) didn't update the screen
  736. * Shift shouldn't cause so much trouble in TCodeEditor now...
  737. * Syntax highlight had problems recognizing a special symbol if it was
  738. prefixed by another symbol character in the source text
  739. * Auto-save also occours at Dos shell, Tool execution, etc. now...
  740. Revision 1.5 1999/06/17 23:45:21 pierre
  741. * dipsoe of S field in TResourceFile destructor
  742. Revision 1.4 1999/04/07 21:56:05 peter
  743. + object support for browser
  744. * html help fixes
  745. * more desktop saving things
  746. * NODEBUG directive to exclude debugger
  747. Revision 1.3 1999/03/23 16:16:43 peter
  748. * linux fixes
  749. Revision 1.2 1999/03/23 15:11:40 peter
  750. * desktop saving things
  751. * vesa mode
  752. * preferences dialog
  753. Revision 1.1 1999/03/16 12:38:18 peter
  754. * tools macro fixes
  755. + tph writer
  756. + first things for resource files
  757. }