resreader.pp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by Giulio Bernardi
  4. Resource reader for .res files
  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 resreader;
  12. {$MODE OBJFPC} {$H+}
  13. interface
  14. uses
  15. Classes, SysUtils, resource;
  16. type
  17. { TResResourceReader }
  18. TResResourceReader = class (TAbstractResourceReader)
  19. private
  20. fExtensions : string;
  21. fDescription : string;
  22. dummyType : TResourceDesc;
  23. dummyName : TResourceDesc;
  24. procedure AlignDword(aStream : TStream);
  25. function ReadResourceHeader(aStream : TStream) : TAbstractResource;
  26. function ReadUnicodeString(aStream : TStream; maxsize : integer; out count : integer) : widestring;
  27. function ReadNameID(aStream : TStream; aDesc : TResourceDesc ;maxsize : integer) : integer;
  28. protected
  29. function GetExtensions : string; override;
  30. function GetDescription : string; override;
  31. procedure Load(aResources : TResources; aStream : TStream); override;
  32. function CheckMagic(aStream : TStream) : boolean; override;
  33. public
  34. constructor Create; override;
  35. destructor Destroy; override;
  36. end;
  37. implementation
  38. uses resdatastream, resfactory;
  39. //This exception is raised when end of stream is detected and expected,
  40. //that is, before reading a new resource header. It's not an error.
  41. type
  42. EResourceReaderEOSReachedException = class (EResourceReaderException);
  43. { TResResourceReader }
  44. procedure TResResourceReader.AlignDword(aStream: TStream);
  45. var toskip : integer;
  46. begin
  47. toskip:=4-(aStream.Position mod 4);
  48. if toskip<>4 then aStream.Seek(toskip,soFromCurrent);
  49. end;
  50. function TResResourceReader.ReadResourceHeader(aStream: TStream
  51. ): TAbstractResource;
  52. var DataSize : longword;
  53. HeaderSize : longword;
  54. toread : longword;
  55. DataVersion : longword;
  56. MemoryFlags : word;
  57. LanguageID : word;
  58. Version : longword;
  59. Characteristics : longword;
  60. DataOffset : longword;
  61. RawData : TResourceDataStream;
  62. begin
  63. try
  64. aStream.ReadBuffer(DataSize,4);
  65. except
  66. on e : EReadError do
  67. raise EResourceReaderEOSReachedException.Create('');
  68. end;
  69. try
  70. aStream.ReadBuffer(HeaderSize,4);
  71. {$IFDEF ENDIAN_BIG}
  72. DataSize:=SwapEndian(DataSize);
  73. HeaderSize:=SwapEndian(HeaderSize);
  74. {$ENDIF}
  75. toread:=headersize-8;
  76. toread:=toread-ReadNameID(aStream,dummyType,toread-18);
  77. toread:=toread-ReadNameID(aStream,dummyName,toread-16);
  78. AlignDword(aStream);
  79. aStream.ReadBuffer(DataVersion,4);
  80. aStream.ReadBuffer(MemoryFlags,2);
  81. aStream.ReadBuffer(LanguageID,2);
  82. aStream.ReadBuffer(Version,4);
  83. aStream.ReadBuffer(Characteristics,4);
  84. {$IFDEF ENDIAN_BIG}
  85. DataVersion:=SwapEndian(DataVersion);
  86. MemoryFlags:=SwapEndian(MemoryFlags);
  87. LanguageID:=SwapEndian(LanguageID);
  88. Version:=SwapEndian(Version);
  89. Characteristics:=SwapEndian(Characteristics);
  90. {$ENDIF}
  91. DataOffset:=aStream.Position;
  92. Result:=TResourceFactory.CreateResource(dummyType,dummyName);
  93. SetDataSize(Result,DataSize);
  94. SetHeaderSize(Result,HeaderSize);
  95. Result.DataVersion:=DataVersion;
  96. Result.MemoryFlags:=MemoryFlags;
  97. Result.LangID:=LanguageID;
  98. Result.Version:=Version;
  99. Result.Characteristics:=Characteristics;
  100. SetDataOffset(Result,DataOffset);
  101. RawData:=TResourceDataStream.Create(aStream,Result,Result.DataSize,TCachedResourceDataStream);
  102. SetRawData(Result,RawData);
  103. if DataSize>0 then
  104. begin
  105. aStream.Seek(DataSize,soFromCurrent);
  106. AlignDword(aStream);
  107. end;
  108. except
  109. on e : EReadError do
  110. raise EResourceReaderUnexpectedEndOfStreamException.Create('');
  111. end;
  112. end;
  113. function TResResourceReader.ReadUnicodeString(aStream: TStream; maxsize: integer;
  114. out count : integer): widestring;
  115. var w : word;
  116. begin
  117. Result:='';
  118. w:=0;
  119. count:=0;
  120. repeat
  121. aStream.ReadBuffer(w,2);
  122. if w<>0 then
  123. begin
  124. {$IFDEF ENDIAN_BIG}
  125. w:=SwapEndian(w);
  126. {$ENDIF}
  127. Result:=Result+widechar(w);
  128. end;
  129. dec(maxsize);
  130. inc(count);
  131. until (w=0) or (maxsize<=0);
  132. count:=count*2;
  133. end;
  134. function TResResourceReader.ReadNameID(aStream : TStream; aDesc : TResourceDesc;
  135. maxsize : integer) : integer;
  136. var tmpw : word;
  137. ws : widestring;
  138. begin
  139. aStream.ReadBuffer(tmpw,2);
  140. {$IFDEF ENDIAN_BIG}
  141. tmpw:=SwapEndian(tmpw);
  142. {$ENDIF}
  143. if tmpw = $FFFF then
  144. begin
  145. aStream.ReadBuffer(tmpw,2);
  146. {$IFDEF ENDIAN_BIG}
  147. tmpw:=SwapEndian(tmpw);
  148. {$ENDIF}
  149. aDesc.ID:=tmpw;
  150. Result:=4;
  151. end
  152. else
  153. begin
  154. ws:=widechar(tmpw)+ReadUnicodeString(aStream,maxsize,Result);
  155. aDesc.Name:=ws;
  156. inc(Result,2);
  157. end;
  158. end;
  159. function TResResourceReader.GetExtensions: string;
  160. begin
  161. Result:=fExtensions;
  162. end;
  163. function TResResourceReader.GetDescription: string;
  164. begin
  165. Result:=fDescription;
  166. end;
  167. procedure TResResourceReader.Load(aResources: TResources; aStream: TStream);
  168. var aRes : TAbstractResource;
  169. begin
  170. if not CheckMagic(aStream) then
  171. raise EResourceReaderWrongFormatException.Create('');
  172. try
  173. while true do
  174. begin
  175. aRes:=ReadResourceHeader(aStream);
  176. try
  177. aResources.Add(aRes);
  178. except
  179. on e : EResourceDuplicateException do
  180. begin
  181. aRes.Free;
  182. raise;
  183. end;
  184. end;
  185. end;
  186. except
  187. on e : EResourceReaderEOSReachedException do ;
  188. end;
  189. end;
  190. function TResResourceReader.CheckMagic(aStream: TStream): boolean;
  191. var lw : longword;
  192. begin
  193. Result:=false;
  194. aStream.ReadBuffer(lw,4); if lw<>0 then exit; //datasize = 0
  195. aStream.ReadBuffer(lw,4);
  196. {$IFDEF ENDIAN_BIG}
  197. lw:=SwapEndian(lw);
  198. {$ENDIF}
  199. if lw<>$20 then exit; //headersize = $20
  200. aStream.ReadBuffer(lw,4);
  201. {$IFDEF ENDIAN_BIG}
  202. lw:=SwapEndian(lw);
  203. {$ENDIF}
  204. if lw<>$FFFF then exit; //type = $0000FFFF
  205. aStream.ReadBuffer(lw,4);
  206. {$IFDEF ENDIAN_BIG}
  207. lw:=SwapEndian(lw);
  208. {$ENDIF}
  209. if lw<>$FFFF then exit; //name = $0000FFFF
  210. aStream.ReadBuffer(lw,4); if lw<>0 then exit; //dataversion = 0
  211. aStream.ReadBuffer(lw,4); if lw<>0 then exit; //memflags,langid = 0
  212. aStream.ReadBuffer(lw,4); if lw<>0 then exit; //version = 0
  213. aStream.ReadBuffer(lw,4); if lw<>0 then exit; //characteristics = 0
  214. Result:=true;
  215. end;
  216. constructor TResResourceReader.Create;
  217. begin
  218. fExtensions:='.res';
  219. fDescription:='.res resource reader';
  220. dummyType:=TResourceDesc.Create;
  221. dummyName:=TResourceDesc.Create;
  222. end;
  223. destructor TResResourceReader.Destroy;
  224. begin
  225. dummyType.Free;
  226. dummyName.Free;
  227. end;
  228. initialization
  229. TResources.RegisterReader('.res',TResResourceReader);
  230. end.