wresourc.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
  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: 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. NamesSize: longint;
  619. procedure AddResourceEntrySize(P: PResourceEntry); {$ifndef FPC}far;{$endif}
  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); {$ifndef FPC}far;{$endif}
  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.
  729. {
  730. $Log$
  731. Revision 1.1 2001-08-04 11:30:26 peter
  732. * ide works now with both compiler versions
  733. Revision 1.1 2000/07/13 09:48:37 michael
  734. + Initial import
  735. Revision 1.11 2000/07/03 08:54:54 pierre
  736. * Some enhancements for WinHelp support by G abor
  737. Revision 1.10 2000/05/16 21:48:13 pierre
  738. * dispose of PBufStream before Fail in TResourceFile.LoadFile and CreateFile
  739. Revision 1.9 2000/04/18 11:42:39 pierre
  740. lot of Gabor changes : see fixes.txt
  741. Revision 1.8 2000/02/07 08:29:14 michael
  742. [*] the fake (!) TOKENS.PAS still contained the typo bug
  743. FSplit(,n,d,e) (correctly FSplit(,d,n,e))
  744. [*] CodeComplete had a very ugly bug - coordinates were document-relative
  745. (instead of being screen-relative)
  746. [*] TResourceStream didn't count the size of the resource names when
  747. determining the file size and this could lead to the last resources not
  748. loaded correctly
  749. [+] Ctrl-Enter in editor now tries to open the file at cursor
  750. [+] CodeComplete option added to Options|Environment|Editor
  751. [+] user interface for managing CodeComplete implemented
  752. [+] user interface for CodeTemplates implemented
  753. [+] CodeComplete wordlist and CodeTemplates stored in desktop file
  754. [+] help topic size no longer limited to 64KB when compiled with FPC
  755. Revision 1.7 1999/09/07 09:26:26 pierre
  756. * E^.DataLen=-1 sets OK to false in TResourceFile.ReadSourceEntryToStream
  757. Revision 1.6 1999/08/03 20:22:44 peter
  758. + TTab acts now on Ctrl+Tab and Ctrl+Shift+Tab...
  759. + Desktop saving should work now
  760. - History saved
  761. - Clipboard content saved
  762. - Desktop saved
  763. - Symbol info saved
  764. * syntax-highlight bug fixed, which compared special keywords case sensitive
  765. (for ex. 'asm' caused asm-highlighting, while 'ASM' didn't)
  766. * with 'whole words only' set, the editor didn't found occourences of the
  767. searched text, if the text appeared previously in the same line, but didn't
  768. satisfied the 'whole-word' condition
  769. * ^QB jumped to (SelStart.X,SelEnd.X) instead of (SelStart.X,SelStart.Y)
  770. (ie. the beginning of the selection)
  771. * when started typing in a new line, but not at the start (X=0) of it,
  772. the editor inserted the text one character more to left as it should...
  773. * TCodeEditor.HideSelection (Ctrl-K+H) didn't update the screen
  774. * Shift shouldn't cause so much trouble in TCodeEditor now...
  775. * Syntax highlight had problems recognizing a special symbol if it was
  776. prefixed by another symbol character in the source text
  777. * Auto-save also occours at Dos shell, Tool execution, etc. now...
  778. Revision 1.5 1999/06/17 23:45:21 pierre
  779. * dipsoe of S field in TResourceFile destructor
  780. Revision 1.4 1999/04/07 21:56:05 peter
  781. + object support for browser
  782. * html help fixes
  783. * more desktop saving things
  784. * NODEBUG directive to exclude debugger
  785. Revision 1.3 1999/03/23 16:16:43 peter
  786. * linux fixes
  787. Revision 1.2 1999/03/23 15:11:40 peter
  788. * desktop saving things
  789. * vesa mode
  790. * preferences dialog
  791. Revision 1.1 1999/03/16 12:38:18 peter
  792. * tools macro fixes
  793. + tph writer
  794. + first things for resource files
  795. }