resource.pp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by Giulio Bernardi
  4. Base classes for resource handling
  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 resource;
  12. {$MODE OBJFPC} {$H+}
  13. interface
  14. uses
  15. Classes, Sysutils;
  16. const
  17. RT_CURSOR = 1; //Hardware-dependent cursor resource.
  18. RT_BITMAP = 2; //Bitmap resource.
  19. RT_ICON = 3; //Hardware-dependent icon resource.
  20. RT_MENU = 4; //Menu resource.
  21. RT_DIALOG = 5; //Dialog box.
  22. RT_STRING = 6; //String-table entry.
  23. RT_FONTDIR = 7; //Font directory resource.
  24. RT_FONT = 8; //Font resource.
  25. RT_ACCELERATOR = 9; //Accelerator table.
  26. RT_RCDATA = 10; //Application-defined resource (raw data).
  27. RT_MESSAGETABLE = 11; //Message-table entry.
  28. RT_GROUP_CURSOR = 12; //Hardware-independent cursor resource.
  29. RT_GROUP_ICON = 14; //Hardware-independent icon resource.
  30. RT_VERSION = 16; //Version resource.
  31. RT_DLGINCLUDE = 17; //Never present in compiled form
  32. RT_PLUGPLAY = 19; //Plug and Play resource.
  33. RT_VXD = 20; //VXD.
  34. RT_ANICURSOR = 21; //Animated cursor.
  35. RT_ANIICON = 22; //Animated icon.
  36. RT_HTML = 23; //HTML.
  37. RT_MANIFEST = 24; //Microsoft Windows XP: Side-by-Side Assembly XML Manifest.
  38. CREATEPROCESS_MANIFEST_RESOURCE_ID = 1;
  39. ISOLATIONAWARE_MANIFEST_RESOURCE_ID = 2;
  40. ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID = 3;
  41. MINIMUM_RESERVED_MANIFEST_RESOURCE_ID = 1; //inclusive
  42. MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID = 16; //inclusive
  43. const
  44. MF_MOVEABLE = $0010;
  45. MF_PURE = $0020;
  46. MF_PRELOAD = $0040;
  47. MF_DISCARDABLE = $1000;
  48. resourcestring
  49. SReaderNotFoundExt = 'Cannot find resource reader for extension ''%s''';
  50. SReaderNotFoundProbe = 'Cannot find a resource reader: unknown format.';
  51. SWriterNotFoundExt = 'Cannot find resource writer for extension ''%s''';
  52. SDescChangeNotAllowed = 'Cannot modify %s resource description';
  53. SLangIDChangeNotAllowed = 'Cannot modify %s resource language ID';
  54. SResDuplicate = 'Duplicate resource: Type = %s, Name = %s, Lang ID = %.4x';
  55. SResourceNotFound = 'Cannot find resource: Type = %s, Name = %s';
  56. SResourceNotFoundLang = 'Cannot find resource: Type = %s, Name = %s, Lang ID = %.4x';
  57. type
  58. TLangID = word;
  59. TResName = string;
  60. TResID = LongWord;
  61. TDescType = (dtName, dtID);
  62. type
  63. EResourceException = class(Exception);
  64. EResourceDescTypeException = class(EResourceException);
  65. EResourceDescChangeNotAllowedException = class(EResourceException);
  66. EResourceLangIDChangeNotAllowedException = class(EResourceException);
  67. EResourceDuplicateException = class(EResourceException);
  68. EResourceNotFoundException = class(EResourceException);
  69. ENoMoreFreeIDsException = class(EResourceException);
  70. EResourceReaderException = class(EResourceException);
  71. EResourceReaderNotFoundException = class(EResourceReaderException);
  72. EResourceReaderWrongFormatException = class(EResourceReaderException);
  73. EResourceReaderUnexpectedEndOfStreamException = class (EResourceReaderException);
  74. EResourceWriterException = class(EResourceException);
  75. EResourceWriterNotFoundException = class(EResourceWriterException);
  76. type
  77. TAbstractResource = class;
  78. { TResourceDesc }
  79. TResourceDesc = class
  80. private
  81. fName : TResName;
  82. fID : TResID;
  83. fDescType : TDescType;
  84. fOwner : TAbstractresource;
  85. function GetID : TResID;
  86. function GetName : TResName;
  87. procedure SetID(const aID : TResID);
  88. procedure SetName(const aName : TResName);
  89. procedure CanChangeType(newType : TDescType);
  90. procedure CanChangeValue;
  91. protected
  92. procedure SetOwner(aOwner : TAbstractResource);
  93. public
  94. constructor Create; overload;
  95. constructor Create(const aID : TResID); overload;
  96. constructor Create(const aName : TResName); overload;
  97. procedure Assign(aResourceDesc : TResourceDesc);
  98. function Equals(aResDesc : TResourceDesc) : boolean;
  99. property Name : TResName read GetName write SetName;
  100. property ID : TResID read GetID write SetID;
  101. property DescType : TDescType read fDescType;
  102. end;
  103. TResources = class;
  104. { TAbstractResource }
  105. TAbstractResource = class
  106. private
  107. fLangId : TLangID;
  108. fDataSize : longword;
  109. fHeaderSize : longword;
  110. fDataVersion : longword;
  111. fMemoryFlags : word;
  112. fVersion : longword;
  113. fCharacteristics : longword;
  114. fDataOffset : longword;
  115. fCodePage : longword;
  116. fRawData : TStream;
  117. fOwnerList : TResources;
  118. fOwner : TAbstractResource;
  119. function GetRawData : TStream;
  120. function GetCacheData : boolean;
  121. procedure SetCacheData(const aValue : boolean);
  122. function GetDataSize : longword;
  123. procedure SetLangID(aLangID : TLangID);
  124. protected
  125. procedure SetDescOwner(aDesc : TResourceDesc);
  126. procedure SetOwnerList(aResources : TResources); virtual;
  127. procedure SetChildOwner(aChild : TAbstractResource);
  128. function GetType : TResourceDesc; virtual; abstract;
  129. function GetName : TResourceDesc; virtual; abstract;
  130. function ChangeDescTypeAllowed(aDesc : TResourceDesc) : boolean; virtual; abstract;
  131. function ChangeDescValueAllowed(aDesc : TResourceDesc) : boolean; virtual; abstract;
  132. procedure NotifyResourcesLoaded; virtual; abstract;
  133. constructor Create; virtual; overload;
  134. public
  135. constructor Create(aType,aName : TResourceDesc); virtual; abstract; overload;
  136. destructor Destroy; override;
  137. function CompareContents(aResource: TAbstractResource): boolean; virtual;
  138. procedure UpdateRawData; virtual; abstract;
  139. procedure SetCustomRawDataStream(aStream : TStream);
  140. property _Type : TResourceDesc read GetType;
  141. property Name : TResourceDesc read GetName;
  142. property LangID : TLangID read fLangID write SetLangID;
  143. property DataSize : longword read GetDataSize;
  144. property HeaderSize : longword read fHeaderSize;
  145. property DataVersion : longword read fDataVersion write fDataVersion;
  146. property MemoryFlags : word read fMemoryFlags write fMemoryFlags;
  147. property Version : longword read fVersion write fVersion;
  148. property Characteristics : longword read fCharacteristics write fCharacteristics;
  149. property DataOffset : longword read fDataOffset;
  150. property CodePage : longword read fCodePage write fCodePage;
  151. property RawData : TStream read GetRawData;
  152. property CacheData : boolean read GetCacheData write SetCacheData;
  153. property OwnerList : TResources read fOwnerList;
  154. property Owner : TAbstractResource read fOwner;
  155. end;
  156. TResourceClass = class of TAbstractResource;
  157. { TGenericResource }
  158. TGenericResource = class(TAbstractResource)
  159. private
  160. fType : TResourceDesc;
  161. fName : TResourceDesc;
  162. protected
  163. function GetType : TResourceDesc; override;
  164. function GetName : TResourceDesc; override;
  165. function ChangeDescTypeAllowed(aDesc : TResourceDesc) : boolean; override;
  166. function ChangeDescValueAllowed(aDesc : TResourceDesc) : boolean; override;
  167. procedure NotifyResourcesLoaded; override;
  168. public
  169. constructor Create(aType,aName : TResourceDesc); override;
  170. destructor Destroy; override;
  171. procedure UpdateRawData; override;
  172. end;
  173. type
  174. TAbstractResourceReader = class;
  175. TAbstractResourceWriter = class;
  176. TResourceReaderClass = class of TAbstractResourceReader;
  177. TResourceWriterClass = class of TAbstractResourceWriter;
  178. { TResources }
  179. TResources = class
  180. private
  181. fList : TFPList;
  182. fTree : TObject;
  183. dummyType : TResourceDesc;
  184. dummyName : TResourceDesc;
  185. fCacheData : boolean;
  186. fMoveFromCount : integer;
  187. fRemovedCount : integer;
  188. function GetItem(index : integer) : TAbstractResource;
  189. function GetCount : longword;
  190. procedure SetCacheData(const aValue : boolean);
  191. procedure NotifyLoaded;
  192. // protected
  193. private
  194. fTempRStream : TStream;
  195. class procedure InitReaderList;
  196. class procedure InitWriterList;
  197. class procedure DisposeStreamerList(aList : TFPList);
  198. class procedure DisposeReaderList;
  199. class procedure DisposeWriterList;
  200. class function FindWriterClass(aExtension : string) : TResourceWriterClass;
  201. class procedure RegisterStreamer(aList : TFPList; aExtension : string; aClass : TClass);
  202. procedure SendUpdateRawData;
  203. procedure InternalRemove(aResource: TAbstractResource);
  204. procedure InternalRemove(aIndex : integer);
  205. procedure QuietRemove(aResource : TAbstractResource; aIndex : integer; aIndexValid : boolean);
  206. procedure InternalClear;
  207. procedure InternalAdd(aResource : TAbstractResource; prevIdx : integer; prevIdxValid : boolean);
  208. procedure AddNoTree(aResource : TAbstractResource);
  209. function InternalFind(aType, aName : TResourceDesc; const aLangID : TLangID) : TAbstractResource; overload;
  210. function InternalFind(aType, aName : TResourceDesc) : TAbstractResource; overload;
  211. procedure BeginMoveFrom;
  212. procedure EndMoveFrom;
  213. public
  214. constructor Create;
  215. destructor Destroy; override;
  216. procedure Add(aResource : TAbstractResource);
  217. function AddAutoID(aResource : TAbstractResource) : TResID;
  218. procedure Clear;
  219. function Find(aType, aName : TResourceDesc; const aLangID : TLangID) : TAbstractResource; overload;
  220. function Find(aType, aName : TResourceDesc) : TAbstractResource; overload;
  221. function Find(const aType : TResName; const aName : TResName; const aLangID : TLangID) : TAbstractResource; overload;
  222. function Find(const aType : TResName; const aName : TResID; const aLangID : TLangID) : TAbstractResource; overload;
  223. function Find(const aType : TResID; const aName : TResName; const aLangID : TLangID) : TAbstractResource; overload;
  224. function Find(const aType : TResID; const aName : TResID; const aLangID : TLangID) : TAbstractResource; overload;
  225. function Find(const aType : TResName; const aName : TResName) : TAbstractResource; overload;
  226. function Find(const aType : TResName; const aName : TResID) : TAbstractResource; overload;
  227. function Find(const aType : TResID; const aName : TResName) : TAbstractResource; overload;
  228. function Find(const aType : TResID; const aName : TResID) : TAbstractResource; overload;
  229. class function FindReader(aStream: TStream; aExtension: string) : TAbstractResourceReader;
  230. class function FindReader(aStream : TStream) : TAbstractResourceReader;
  231. procedure MoveFrom(aResources : TResources);
  232. function Remove(aType,aName : TResourceDesc; const aLangID : TLangID) : TAbstractResource; overload;
  233. function Remove(aType,aName : TResourceDesc) : TAbstractResource; overload;
  234. function Remove(aResource : TAbstractResource) : TAbstractResource; overload;
  235. function Remove(aIndex : integer) : TAbstractResource; overload;
  236. procedure LoadFromStream(aStream : TStream); overload;
  237. procedure LoadFromStream(aStream : TStream; aReader : TAbstractResourceReader); overload;
  238. procedure LoadFromFile(aFileName : string); overload;
  239. procedure LoadFromFile(aFileName : string; aReader : TAbstractResourceReader); overload;
  240. class procedure RegisterReader(const aExtension : string; aClass : TResourceReaderClass);
  241. class procedure RegisterWriter(const aExtension : string; aClass : TResourceWriterClass);
  242. procedure WriteToStream(aStream : TStream; aWriter : TAbstractResourceWriter);
  243. procedure WriteToFile(aFileName : string); overload;
  244. procedure WriteToFile(aFileName : string; aWriter : TAbstractResourceWriter); overload;
  245. property Count : longword read GetCount;
  246. property Items[Index : integer] : TAbstractResource read GetItem; default;
  247. property CacheData : boolean read fCacheData write SetCacheData;
  248. end;
  249. { TAbstractResourceReader }
  250. TAbstractResourceReader = class
  251. private
  252. protected
  253. procedure SetDataSize(aResource : TAbstractResource; aValue : longword);
  254. procedure SetHeaderSize(aResource : TAbstractResource; aValue : longword);
  255. procedure SetDataOffset(aResource : TAbstractResource; aValue : longword);
  256. procedure SetRawData(aResource : TAbstractResource; aStream : TStream);
  257. procedure CallSubReaderLoad(aReader: TAbstractResourceReader; aResources : TResources; aStream : TStream);
  258. procedure AddNoTree(aResources : TResources; aResource: TAbstractResource);
  259. function GetTree(aResources : TResources) : TObject;
  260. function GetExtensions : string; virtual; abstract;
  261. function GetDescription : string; virtual; abstract;
  262. procedure Load(aResources : TResources; aStream : TStream); virtual; abstract;
  263. function CheckMagic(aStream : TStream) : boolean; virtual; abstract;
  264. public
  265. constructor Create; virtual; abstract;
  266. property Extensions : string read GetExtensions;
  267. property Description : string read GetDescription;
  268. end;
  269. { TAbstractResourceWriter }
  270. TAbstractResourceWriter = class
  271. private
  272. protected
  273. function GetTree(aResources : TResources) : TObject;
  274. function GetExtensions : string; virtual; abstract;
  275. function GetDescription : string; virtual; abstract;
  276. procedure Write(aResources : TResources; aStream : TStream); virtual; abstract;
  277. public
  278. constructor Create; virtual; abstract;
  279. property Extensions : string read GetExtensions;
  280. property Description : string read GetDescription;
  281. end;
  282. implementation
  283. uses resdatastream, resourcetree, resmerger;
  284. type
  285. PRegisteredStreamerEntry = ^TRegisteredStreamerEntry;
  286. TRegisteredStreamerEntry = record
  287. ext : shortstring;
  288. _class : TClass;
  289. next : PRegisteredStreamerEntry;
  290. end;
  291. var RegisteredReaders : TFPList = nil;
  292. RegisteredWriters : TFPList = nil;
  293. { TResourceDesc }
  294. function TResourceDesc.GetID: TResID;
  295. begin
  296. if fDescType<>dtId then
  297. raise EResourceDescTypeException.Create('');
  298. Result:=fId;
  299. end;
  300. function TResourceDesc.GetName: TResName;
  301. begin
  302. if fDescType = dtName then
  303. Result:=fName
  304. else Result:=IntToStr(fId);
  305. end;
  306. procedure TResourceDesc.CanChangeType(newType : TDescType);
  307. begin
  308. if fOwner=nil then exit;
  309. if newType=fDescType then exit;
  310. if (fOwner.OwnerList<>nil) or (not fOwner.ChangeDescTypeAllowed(self)) then
  311. raise EResourceDescChangeNotAllowedException.CreateFmt(SDescChangeNotAllowed,[Name]);
  312. end;
  313. procedure TResourceDesc.CanChangeValue;
  314. begin
  315. if fOwner=nil then exit;
  316. if (fOwner.OwnerList<>nil) or (not fOwner.ChangeDescValueAllowed(self)) then
  317. raise EResourceDescChangeNotAllowedException.CreateFmt(SDescChangeNotAllowed,[Name]);
  318. end;
  319. procedure TResourceDesc.SetOwner(aOwner: TAbstractResource);
  320. begin
  321. fOwner:=aOwner;
  322. end;
  323. procedure TResourceDesc.SetID(const aID: TResID);
  324. begin
  325. CanChangeType(dtID);
  326. CanChangeValue;
  327. fDescType:=dtID;
  328. fId:=aID;
  329. end;
  330. procedure TResourceDesc.SetName(const aName: TResName);
  331. begin
  332. CanChangeType(dtName);
  333. CanChangeValue;
  334. fDescType:=dtName;
  335. fName:=UpperCase(aName);
  336. end;
  337. constructor TResourceDesc.Create;
  338. begin
  339. fName:='';
  340. fID:=0;
  341. fDescType:=dtName;
  342. fOwner:=nil;
  343. end;
  344. constructor TResourceDesc.Create(const aID: TResID);
  345. begin
  346. Create;
  347. SetID(aID);
  348. end;
  349. constructor TResourceDesc.Create(const aName: TResName);
  350. begin
  351. Create;
  352. SetName(aName);
  353. end;
  354. procedure TResourceDesc.Assign(aResourceDesc: TResourceDesc);
  355. begin
  356. CanChangeType(aResourceDesc.fDescType);
  357. CanChangeValue;
  358. fDescType:=aResourceDesc.fDescType;
  359. case fDescType of
  360. dtID : begin fID:=aResourceDesc.fID; fName:=''; end;
  361. dtName : begin fName:=aResourceDesc.fName; fID:=0; end;
  362. end;
  363. end;
  364. function TResourceDesc.Equals(aResDesc: TResourceDesc): boolean;
  365. begin
  366. Result:=aResDesc.DescType=fDescType;
  367. if not Result then exit;
  368. case fDescType of
  369. dtName : Result:=aResDesc.Name=fName;
  370. dtID : Result:=aResDesc.ID=fID;
  371. end;
  372. end;
  373. { TAbstractResource }
  374. function TAbstractResource.GetRawData: TStream;
  375. begin
  376. if fRawData = nil then
  377. fRawData:=TResourceDataStream.Create(nil,self,DataSize,TCachedResourceDataStream);
  378. Result:=fRawData;
  379. end;
  380. function TAbstractResource.GetCacheData: boolean;
  381. begin
  382. Result:=TResourceDataStream(RawData).Cached;
  383. end;
  384. procedure TAbstractResource.SetCacheData(const aValue: boolean);
  385. begin
  386. TResourceDataStream(RawData).Cached:=aValue;
  387. end;
  388. function TAbstractResource.GetDataSize: longword;
  389. begin
  390. if fRawData=nil then Result:=fDataSize
  391. else Result:=fRawData.Size;
  392. end;
  393. procedure TAbstractResource.SetLangID(aLangID: TLangID);
  394. begin
  395. if OwnerList<>nil then
  396. raise EResourceLangIDChangeNotAllowedException.CreateFmt(SLangIDChangeNotAllowed,[Name]);
  397. fLangId:=aLangID;
  398. end;
  399. procedure TAbstractResource.SetDescOwner(aDesc: TResourceDesc);
  400. begin
  401. aDesc.SetOwner(self);
  402. end;
  403. procedure TAbstractResource.SetOwnerList(aResources: TResources);
  404. begin
  405. fOwnerList:=aResources;
  406. end;
  407. procedure TAbstractResource.SetChildOwner(aChild: TAbstractResource);
  408. begin
  409. aChild.fOwner:=self;
  410. end;
  411. constructor TAbstractResource.Create;
  412. begin
  413. fLangID:=0;
  414. fDataSize:=0;
  415. fHeaderSize:=0;
  416. fDataVersion:=0;
  417. fMemoryFlags:=MF_MOVEABLE or MF_DISCARDABLE;
  418. fVersion:=0;
  419. fCharacteristics:=0;
  420. fDataOffset:=0;
  421. fCodePage:=0;
  422. fRawData:=nil;
  423. fOwnerList:=nil;
  424. fOwner:=nil;
  425. end;
  426. destructor TAbstractResource.Destroy;
  427. begin
  428. if fRawData<>nil then fRawData.Free;
  429. end;
  430. function TAbstractResource.CompareContents(aResource: TAbstractResource
  431. ): boolean;
  432. begin
  433. Result:=TResourceDataStream(RawData).Compare(aResource.RawData);
  434. end;
  435. procedure TAbstractResource.SetCustomRawDataStream(aStream: TStream);
  436. begin
  437. TResourceDataStream(RawData).SetCustomStream(aStream);
  438. end;
  439. { TResources }
  440. function TResources.GetItem(index: integer): TAbstractResource;
  441. begin
  442. Result:=TAbstractResource(fList[index]);
  443. end;
  444. function TResources.GetCount: longword;
  445. begin
  446. Result:=fList.Count;
  447. end;
  448. procedure TResources.SetCacheData(const aValue: boolean);
  449. var i : integer;
  450. begin
  451. if aValue=fCacheData then exit;
  452. fCacheData:=aValue;
  453. if fCacheData then exit; //single resources cache data by default
  454. //don't cache data: load everything and free the temporary stream.
  455. for i:=0 to Count-1 do
  456. Items[i].CacheData:=aValue;
  457. if fTempRStream<>nil then FreeAndNil(fTempRStream);
  458. end;
  459. procedure TResources.NotifyLoaded;
  460. var i : integer;
  461. begin
  462. for i:=0 to Count-1 do
  463. Items[i].NotifyResourcesLoaded;
  464. end;
  465. class procedure TResources.InitReaderList;
  466. begin
  467. if RegisteredReaders=nil then
  468. RegisteredReaders:=TFPList.Create;
  469. end;
  470. class procedure TResources.InitWriterList;
  471. begin
  472. if RegisteredWriters=nil then
  473. RegisteredWriters:=TFPList.Create;
  474. end;
  475. class procedure TResources.DisposeStreamerList(aList: TFPList);
  476. var p,p2 : PRegisteredStreamerEntry;
  477. i : integer;
  478. begin
  479. if aList=nil then exit;
  480. for i:=0 to aList.Count-1 do
  481. begin
  482. p:=PRegisteredStreamerEntry(aList[i]);
  483. while p<>nil do
  484. begin
  485. p2:=p^.next;
  486. Freemem(p);
  487. p:=p2;
  488. end;
  489. end;
  490. end;
  491. class procedure TResources.DisposeReaderList;
  492. begin
  493. DisposeStreamerList(RegisteredReaders);
  494. FreeAndNil(RegisteredReaders);
  495. end;
  496. class procedure TResources.DisposeWriterList;
  497. begin
  498. DisposeStreamerList(RegisteredWriters);
  499. FreeAndNil(RegisteredWriters);
  500. end;
  501. class function TResources.FindReader(aStream: TStream; aExtension: string) :
  502. TAbstractResourceReader;
  503. var i : integer;
  504. p : PRegisteredStreamerEntry;
  505. position : int64;
  506. found : boolean;
  507. begin
  508. Result:=nil;
  509. InitReaderList;
  510. position:=aStream.Position;
  511. aExtension:=lowercase(aExtension);
  512. for i:=0 to RegisteredReaders.Count-1 do
  513. begin
  514. p:=PRegisteredStreamerEntry(RegisteredReaders[i]);
  515. if p^.ext=aExtension then //try all readers registered for this extension
  516. begin
  517. while p<>nil do
  518. begin
  519. Result:=TResourceReaderClass(p^._class).Create;
  520. found:=Result.CheckMagic(aStream);
  521. aStream.Position:=position; //rewind
  522. if found then exit;
  523. FreeAndNil(Result);
  524. p:=p^.next;
  525. end;
  526. // There are readers for this extension, but no one seems to be able
  527. // to read the file.
  528. // So, return the first reader, and it will fail later.
  529. p:=PRegisteredStreamerEntry(RegisteredReaders[i]);
  530. Result:=TResourceReaderClass(p^._class).Create;
  531. exit;
  532. end;
  533. end;
  534. raise EResourceReaderNotFoundException.Create(Format(SReaderNotFoundExt,[aExtension]));
  535. end;
  536. class function TResources.FindReader(aStream: TStream
  537. ): TAbstractResourceReader;
  538. var i : integer;
  539. p : PRegisteredStreamerEntry;
  540. position : int64;
  541. found : boolean;
  542. begin
  543. Result:=nil;
  544. InitReaderList;
  545. position:=aStream.Position;
  546. for i:=0 to RegisteredReaders.Count-1 do
  547. begin
  548. p:=PRegisteredStreamerEntry(RegisteredReaders[i]);
  549. while p<>nil do
  550. begin
  551. Result:=TResourceReaderClass(p^._class).Create;
  552. found:=Result.CheckMagic(aStream);
  553. aStream.Position:=position; //rewind
  554. if found then exit;
  555. FreeAndNil(Result);
  556. p:=p^.next;
  557. end;
  558. end;
  559. raise EResourceReaderNotFoundException.Create(SReaderNotFoundProbe);
  560. end;
  561. procedure TResources.MoveFrom(aResources: TResources);
  562. var res : TAbstractResource;
  563. i : integer;
  564. begin
  565. aResources.BeginMoveFrom;
  566. try
  567. for i:=0 to aResources.Count-1 do
  568. begin
  569. res:=aResources.Items[i];
  570. if res=nil then continue;
  571. if res.Owner<>nil then //If we are adding an owned resource, add
  572. InternalAdd(res.Owner,0,false) //the owner resource instead (it will take
  573. else //care of adding its sub-resources)
  574. InternalAdd(res,i,true);
  575. end;
  576. finally
  577. aResources.EndMoveFrom;
  578. end;
  579. end;
  580. class function TResources.FindWriterClass(aExtension: string
  581. ): TResourceWriterClass;
  582. var i : integer;
  583. p : PRegisteredStreamerEntry;
  584. begin
  585. Result:=nil;
  586. InitWriterList;
  587. aExtension:=lowercase(aExtension);
  588. for i:=0 to RegisteredWriters.Count-1 do
  589. begin
  590. p:=PRegisteredStreamerEntry(RegisteredWriters[i]);
  591. if p^.ext=aExtension then
  592. begin
  593. Result:=TResourceWriterClass(p^._class);
  594. exit;
  595. end;
  596. end;
  597. raise EResourceWriterNotFoundException.Create(Format(SWriterNotFoundExt,[aExtension]));
  598. end;
  599. procedure TResources.InternalAdd(aResource : TAbstractResource; prevIdx :
  600. integer; prevIdxValid : boolean);
  601. var resold : TAbstractResource;
  602. begin
  603. resold:=InternalFind(aResource._Type,aResource.Name,aResource.LangID);
  604. if resold<>nil then
  605. begin
  606. if TResourceMerger.Merge(resold,aResource) then exit;
  607. raise EResourceDuplicateException.CreateFmt(SResDuplicate,[aResource._Type.Name,aResource.Name.Name,aResource.LangID]);
  608. end;
  609. fList.Add(aResource);
  610. TRootResTreeNode(fTree).Add(aResource);
  611. if aResource.OwnerList<>nil then
  612. aResource.OwnerList.QuietRemove(aResource,prevIdx,prevIdxValid);
  613. aResource.SetOwnerList(self);
  614. aResource.CacheData:=fCacheData;
  615. end;
  616. procedure TResources.AddNoTree(aResource: TAbstractResource);
  617. begin
  618. fList.Add(aResource);
  619. aResource.SetOwnerList(self);
  620. aResource.CacheData:=fCacheData;
  621. end;
  622. function TResources.InternalFind(aType, aName: TResourceDesc;
  623. const aLangID: TLangID): TAbstractResource;
  624. begin
  625. Result:=TRootResTreeNode(fTree).Find(aType,aName,aLangID);
  626. end;
  627. function TResources.InternalFind(aType, aName: TResourceDesc
  628. ): TAbstractResource;
  629. begin
  630. Result:=TRootResTreeNode(fTree).Find(aType,aName);
  631. end;
  632. procedure TResources.BeginMoveFrom;
  633. begin
  634. inc(fMoveFromCount);
  635. fRemovedCount:=0;
  636. end;
  637. procedure TResources.EndMoveFrom;
  638. begin
  639. dec(fMoveFromCount);
  640. if fMoveFromCount=0 then
  641. if fRemovedCount=fList.Count then //all items removed: clear the list
  642. fList.Clear
  643. else
  644. fList.Pack; //for some reason, not all items were removed. remove only nils
  645. end;
  646. procedure TResources.Add(aResource: TAbstractResource);
  647. begin
  648. InternalAdd(aResource,0,false);
  649. end;
  650. function TResources.AddAutoID(aResource: TAbstractResource): TResID;
  651. var newid : TResID;
  652. begin
  653. newid:=TRootResTreeNode(fTree).FindFreeID(aResource._Type);
  654. //if we reached this point, ENoMoreFreeIDsException hasn't been raised.
  655. if aResource.OwnerList<>nil then aResource.OwnerList.Remove(aResource);
  656. aResource.Name.ID:=newid;
  657. InternalAdd(aResource,0,false);
  658. Result:=newid;
  659. end;
  660. //clear without freeing fTempRStream
  661. procedure TResources.InternalClear;
  662. var i : integer;
  663. begin
  664. TRootResTreeNode(fTree).Clear;
  665. for i:=0 to Count-1 do
  666. TAbstractResource(fList[i]).Free;
  667. fList.Clear;
  668. end;
  669. procedure TResources.Clear;
  670. begin
  671. InternalClear;
  672. if fTempRStream<>nil then FreeAndNil(fTempRStream);
  673. end;
  674. function TResources.Find(aType, aName: TResourceDesc; const aLangID : TLangID):
  675. TAbstractResource;
  676. begin
  677. Result:=TRootResTreeNode(fTree).Find(aType,aName,aLangID);
  678. if Result=nil then
  679. raise EResourceNotFoundException.CreateFmt(SResourceNotFoundLang,[aType.Name,aName.Name,aLangID]);
  680. end;
  681. function TResources.Find(aType, aName: TResourceDesc):
  682. TAbstractResource;
  683. begin
  684. Result:=TRootResTreeNode(fTree).Find(aType,aName);
  685. if Result=nil then
  686. raise EResourceNotFoundException.CreateFmt(SResourceNotFound,[aType.Name,aName.Name]);
  687. end;
  688. function TResources.Find(const aType: TResName; const aName: TResName; const
  689. aLangID : TLangID): TAbstractResource;
  690. begin
  691. dummyType.Name:=aType;
  692. dummyName.Name:=aName;
  693. Result:=Find(dummyType,dummyName,aLangID);
  694. end;
  695. function TResources.Find(const aType: TResName; const aName: TResID; const
  696. aLangID : TLangID ): TAbstractResource;
  697. begin
  698. dummyType.Name:=aType;
  699. dummyName.ID:=aName;
  700. Result:=Find(dummyType,dummyName,aLangID);
  701. end;
  702. function TResources.Find(const aType: TResID; const aName: TResName; const
  703. aLangID : TLangID ): TAbstractResource;
  704. begin
  705. dummyType.ID:=aType;
  706. dummyName.Name:=aName;
  707. Result:=Find(dummyType,dummyName,aLangID);
  708. end;
  709. function TResources.Find(const aType: TResID; const aName: TResID; const
  710. aLangID : TLangID ): TAbstractResource;
  711. begin
  712. dummyType.ID:=aType;
  713. dummyName.ID:=aName;
  714. Result:=Find(dummyType,dummyName,aLangID);
  715. end;
  716. function TResources.Find(const aType: TResName; const aName: TResName
  717. ): TAbstractResource;
  718. begin
  719. dummyType.Name:=aType;
  720. dummyName.Name:=aName;
  721. Result:=Find(dummyType,dummyName);
  722. end;
  723. function TResources.Find(const aType: TResName; const aName: TResID
  724. ): TAbstractResource;
  725. begin
  726. dummyType.Name:=aType;
  727. dummyName.ID:=aName;
  728. Result:=Find(dummyType,dummyName);
  729. end;
  730. function TResources.Find(const aType: TResID; const aName: TResName
  731. ): TAbstractResource;
  732. begin
  733. dummyType.ID:=aType;
  734. dummyName.Name:=aName;
  735. Result:=Find(dummyType,dummyName);
  736. end;
  737. function TResources.Find(const aType: TResID; const aName: TResID
  738. ): TAbstractResource;
  739. begin
  740. dummyType.ID:=aType;
  741. dummyName.ID:=aName;
  742. Result:=Find(dummyType,dummyName);
  743. end;
  744. function TResources.Remove(aType,aName : TResourceDesc;
  745. const aLangID : TLangID) : TAbstractResource;
  746. begin
  747. Result:=TRootResTreeNode(fTree).Remove(aType,aName,aLangID);
  748. InternalRemove(Result);
  749. Result.SetOwnerList(nil);
  750. end;
  751. function TResources.Remove(aType,aName : TResourceDesc) : TAbstractResource;
  752. begin
  753. Result:=TRootResTreeNode(fTree).Remove(aType,aName);
  754. InternalRemove(Result);
  755. Result.SetOwnerList(nil);
  756. end;
  757. function TResources.Remove(aResource: TAbstractResource) : TAbstractResource;
  758. begin
  759. InternalRemove(aResource);
  760. Result:=TRootResTreeNode(fTree).Remove(aResource._Type,aResource.Name,aResource.LangID);
  761. Result.SetOwnerList(nil);
  762. end;
  763. function TResources.Remove(aIndex: integer): TAbstractResource;
  764. begin
  765. Result:=Items[aIndex];
  766. InternalRemove(aIndex);
  767. Result:=TRootResTreeNode(fTree).Remove(Result._Type,Result.Name,Result.LangID);
  768. Result.SetOwnerList(nil);
  769. end;
  770. procedure TResources.InternalRemove(aResource: TAbstractResource);
  771. var idx : integer;
  772. begin
  773. if aResource=nil then exit;
  774. idx:=fList.IndexOf(aResource);
  775. if idx=-1 then
  776. raise EResourceNotFoundException.CreateFmt(SResourceNotFoundLang,[
  777. aResource._Type.Name,aResource.Name.Name,aResource.LangID]);
  778. if fMoveFromCount>0 then fList[idx]:=nil
  779. else fList.Delete(idx);
  780. inc(fRemovedCount);
  781. end;
  782. procedure TResources.InternalRemove(aIndex: integer);
  783. begin
  784. if fMoveFromCount>0 then fList[aIndex]:=nil
  785. else fList.Delete(aIndex);
  786. inc(fRemovedCount);
  787. end;
  788. //removes without calling setownerlist
  789. procedure TResources.QuietRemove(aResource : TAbstractResource; aIndex :
  790. integer; aIndexValid : boolean);
  791. begin
  792. if aIndexValid then InternalRemove(aIndex)
  793. else InternalRemove(aResource);
  794. TRootResTreeNode(fTree).Remove(aResource._Type,aResource.Name,aResource.LangID);
  795. end;
  796. procedure TResources.LoadFromStream(aStream: TStream);
  797. var aReader : TAbstractResourceReader;
  798. begin
  799. aReader:=FindReader(aStream);
  800. try
  801. LoadFromStream(aStream,aReader);
  802. finally
  803. aReader.Free;
  804. end;
  805. end;
  806. procedure TResources.LoadFromStream(aStream: TStream;
  807. aReader: TAbstractResourceReader);
  808. begin
  809. InternalClear;
  810. aReader.Load(self,aStream);
  811. NotifyLoaded;
  812. end;
  813. procedure TResources.LoadFromFile(aFileName: string);
  814. var ext : string;
  815. aReader : TAbstractResourceReader;
  816. begin
  817. ext:=ExtractFileExt(aFileName);
  818. if fTempRStream<>nil then FreeAndNil(fTempRStream);
  819. fTempRStream:=TFileStream.Create(aFileName,fmOpenRead or fmShareDenyWrite);
  820. aReader:=FindReader(fTempRStream,ext);
  821. try
  822. LoadFromStream(fTempRStream,aReader);
  823. finally
  824. aReader.Free;
  825. if not fCacheData then FreeAndNil(fTempRStream);
  826. end;
  827. end;
  828. procedure TResources.LoadFromFile(aFileName: string;
  829. aReader: TAbstractResourceReader);
  830. begin
  831. if fTempRStream<>nil then FreeAndNil(fTempRStream);
  832. fTempRStream:=TFileStream.Create(aFileName,fmOpenRead or fmShareDenyWrite);
  833. try
  834. LoadFromStream(fTempRStream,aReader);
  835. finally
  836. if not fCacheData then FreeAndNil(fTempRStream);
  837. end;
  838. end;
  839. class procedure TResources.RegisterStreamer(aList : TFPList; aExtension :
  840. string; aClass : TClass);
  841. var newp,p : PRegisteredStreamerEntry;
  842. i : integer;
  843. begin
  844. aExtension:=lowercase(aExtension);
  845. newp:=GetMem(sizeof(TRegisteredStreamerEntry));
  846. newp^.next:=nil;
  847. newp^.ext:=aExtension;
  848. newp^._class:=aClass;
  849. for i:=0 to aList.Count-1 do
  850. begin
  851. p:=PRegisteredStreamerEntry(aList[i]);
  852. if p^.ext=aExtension then
  853. begin
  854. while p^.next<>nil do
  855. p:=p^.next;
  856. p^.next:=newp;
  857. exit;
  858. end;
  859. end;
  860. aList.Add(newp);
  861. end;
  862. procedure TResources.SendUpdateRawData;
  863. var i : integer;
  864. begin
  865. for i:=0 to Count-1 do
  866. Items[i].UpdateRawData;
  867. end;
  868. class procedure TResources.RegisterReader(const aExtension : string;
  869. aClass: TResourceReaderClass);
  870. begin
  871. InitReaderList;
  872. RegisterStreamer(RegisteredReaders,aExtension,aClass);
  873. end;
  874. class procedure TResources.RegisterWriter(const aExtension : string;
  875. aClass: TResourceWriterClass);
  876. begin
  877. InitWriterList;
  878. RegisterStreamer(RegisteredWriters,aExtension,aClass);
  879. end;
  880. procedure TResources.WriteToStream(aStream: TStream;
  881. aWriter: TAbstractResourceWriter);
  882. begin
  883. SendUpdateRawData;
  884. aWriter.Write(self,aStream);
  885. end;
  886. procedure TResources.WriteToFile(aFileName: string);
  887. var ext : string;
  888. aWriter : TAbstractResourceWriter;
  889. begin
  890. ext:=ExtractFileExt(aFileName);
  891. aWriter:=FindWriterClass(ext).Create;
  892. try
  893. WriteToFile(aFileName,aWriter);
  894. finally
  895. aWriter.Free;
  896. end;
  897. end;
  898. procedure TResources.WriteToFile(aFileName: string;
  899. aWriter: TAbstractResourceWriter);
  900. var OutStream : TFileStream;
  901. begin
  902. OutStream:=TFileStream.Create(aFileName,fmCreate or fmShareDenyWrite);
  903. try
  904. WriteToStream(OutStream,aWriter);
  905. finally
  906. OutStream.Free;
  907. end;
  908. end;
  909. constructor TResources.Create;
  910. begin
  911. fList:=TFPList.Create;
  912. fTree:=TRootResTreeNode.Create;
  913. dummyType:=TResourceDesc.Create;
  914. dummyName:=TResourceDesc.Create;
  915. fTempRStream:=nil;
  916. fCacheData:=true;
  917. fMoveFromCount:=0;
  918. fRemovedCount:=0;
  919. end;
  920. destructor TResources.Destroy;
  921. begin
  922. Clear;
  923. fList.Free;
  924. fTree.Free;
  925. dummyType.Free;
  926. dummyName.Free;
  927. end;
  928. { TAbstractResourceReader }
  929. procedure TAbstractResourceReader.SetDataSize(aResource: TAbstractResource;
  930. aValue: longword);
  931. begin
  932. aResource.fDataSize:=aValue;
  933. end;
  934. procedure TAbstractResourceReader.SetHeaderSize(aResource: TAbstractResource;
  935. aValue: longword);
  936. begin
  937. aResource.fHeaderSize:=aValue;
  938. end;
  939. procedure TAbstractResourceReader.SetDataOffset(aResource: TAbstractResource;
  940. aValue: longword);
  941. begin
  942. aResource.fDataOffset:=aValue;
  943. end;
  944. procedure TAbstractResourceReader.SetRawData(aResource: TAbstractResource;
  945. aStream: TStream);
  946. begin
  947. if aResource.fRawData<>nil then aResource.fRawData.Free; //should never happen!
  948. aResource.fRawData:=aStream;
  949. end;
  950. procedure TAbstractResourceReader.CallSubReaderLoad(
  951. aReader: TAbstractResourceReader; aResources: TResources; aStream: TStream);
  952. begin
  953. aReader.Load(aResources,aStream);
  954. end;
  955. procedure TAbstractResourceReader.AddNoTree(aResources: TResources;
  956. aResource: TAbstractResource);
  957. begin
  958. aResources.AddNoTree(aResource);
  959. end;
  960. function TAbstractResourceReader.GetTree(aResources: TResources): TObject;
  961. begin
  962. Result:=aResources.fTree;
  963. end;
  964. { TGenericResource }
  965. function TGenericResource.ChangeDescTypeAllowed(aDesc: TResourceDesc): boolean;
  966. begin
  967. Result:=true;
  968. end;
  969. function TGenericResource.ChangeDescValueAllowed(aDesc: TResourceDesc
  970. ): boolean;
  971. begin
  972. Result:=true;
  973. end;
  974. procedure TGenericResource.NotifyResourcesLoaded;
  975. begin
  976. end;
  977. procedure TGenericResource.UpdateRawData;
  978. begin
  979. end;
  980. function TGenericResource.GetType : TResourceDesc;
  981. begin
  982. Result:=fType;
  983. end;
  984. function TGenericResource.GetName : TResourceDesc;
  985. begin
  986. Result:=fName;
  987. end;
  988. constructor TGenericResource.Create(aType, aName: TResourceDesc);
  989. begin
  990. Create;
  991. fType:=TResourceDesc.Create;
  992. fType.Assign(aType);
  993. fName:=TResourceDesc.Create;
  994. fName.Assign(aName);
  995. SetDescOwner(fType);
  996. SetDescOwner(fName);
  997. end;
  998. destructor TGenericResource.Destroy;
  999. begin
  1000. fName.Free;
  1001. fType.Free;
  1002. inherited Destroy;
  1003. end;
  1004. { TAbstractResourceWriter }
  1005. function TAbstractResourceWriter.GetTree(aResources: TResources): TObject;
  1006. begin
  1007. Result:=aResources.fTree;
  1008. end;
  1009. finalization
  1010. TResources.DisposeReaderList;
  1011. TResources.DisposeWriterList;
  1012. end.