GLApplicationFileIO.pas 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. (*
  5. Components and functions that abstract file I/O access for an application.
  6. Allows re-routing file reads to reads from a single archive file f.i.
  7. *)
  8. unit GLApplicationFileIO;
  9. interface
  10. {$I GLScene.inc}
  11. uses
  12. Winapi.Windows,
  13. System.Classes,
  14. System.SysUtils,
  15. GLBaseClasses,
  16. GLSLog;
  17. const
  18. GLS_RC_DDS_Type = RT_RCDATA;
  19. GLS_RC_JPG_Type = RT_RCDATA;
  20. GLS_RC_XML_Type = RT_RCDATA;
  21. GLS_RC_String_Type = RT_RCDATA;
  22. type
  23. TGLApplicationResource = (
  24. aresNone,
  25. aresSplash,
  26. aresTexture,
  27. aresMaterial,
  28. aresSampler,
  29. aresFont,
  30. aresMesh);
  31. TAFIOCreateFileStream = function(const fileName: string; mode: Word): TStream;
  32. TAFIOFileStreamExists = function(const fileName: string): Boolean;
  33. TAFIOFileStreamEvent = procedure (const fileName : String; mode : Word; var Stream : TStream) of object;
  34. TAFIOFileStreamExistsEvent = function(const fileName:string): Boolean of object;
  35. (* Allows specifying a custom behaviour for CreateFileStream.
  36. The component should be considered a helper only, you can directly specify
  37. a function via the vAFIOCreateFileStream variable.
  38. If multiple ApplicationFileIO components exist in the application,
  39. the last one created will be the active one. *)
  40. TGLApplicationFileIO = class(TComponent)
  41. private
  42. FOnFileStream: TAFIOFileStreamEvent;
  43. FOnFileStreamExists: TAFIOFileStreamExistsEvent;
  44. public
  45. constructor Create(AOwner: TComponent); override;
  46. destructor Destroy; override;
  47. published
  48. (* Event that allows you to specify a stream for the file.
  49. Destruction of the stream is at the discretion of the code that
  50. invoked CreateFileStream. Return nil to let the default mechanism
  51. take place (ie. attempt a regular file system access). *)
  52. property OnFileStream: TAFIOFileStreamEvent read FOnFileStream write FOnFileStream;
  53. // Event that allows you to specify if a stream for the file exists.
  54. property OnFileStreamExists: TAFIOFileStreamExistsEvent read FOnFileStreamExists write FOnFileStreamExists;
  55. end;
  56. TGLDataFileCapability = (dfcRead, dfcWrite);
  57. TGLDataFileCapabilities = set of TGLDataFileCapability;
  58. (* Abstract base class for data file formats interfaces.
  59. This class declares base file-related behaviours, ie. ability to load/save
  60. from a file or a stream.
  61. It is highly recommended to overload ONLY the stream based methods, as the
  62. file-based one just call these, and stream-based behaviours allow for more
  63. enhancement (such as other I/O abilities, compression, cacheing, etc.)
  64. to this class, without the need to rewrite subclasses. *)
  65. TGLDataFile = class(TGLUpdateAbleObject)
  66. private
  67. FResourceName: string;
  68. procedure SetResourceName(const AName: string);
  69. public
  70. // Describes what the TGLDataFile is capable of. Default value is [dfcRead].
  71. class function Capabilities: TGLDataFileCapabilities; virtual;
  72. // Duplicates Self and returns a copy. Subclasses should override this method to duplicate their data.
  73. function CreateCopy(AOwner: TPersistent): TGLDataFile; virtual;
  74. procedure LoadFromFile(const fileName: string); virtual;
  75. procedure SaveToFile(const fileName: string); virtual;
  76. procedure LoadFromStream(stream: TStream); virtual;
  77. procedure SaveToStream(stream: TStream); virtual;
  78. procedure Initialize; virtual;
  79. { Optionnal resource name.
  80. When using LoadFromFile/SaveToFile, the filename is placed in it,
  81. when using the Stream variants, the caller may place the resource
  82. name in it for parser use. }
  83. property ResourceName: string read FResourceName write SetResourceName;
  84. end;
  85. TGLDataFileClass = class of TGLDataFile;
  86. TGLResourceStream = TResourceStream;
  87. // Returns true if an ApplicationFileIO has been defined
  88. function ApplicationFileIODefined: Boolean;
  89. (*Creates a file stream corresponding to the fileName.
  90. If the file does not exists, an exception will be triggered.
  91. Default mechanism creates a regular TFileStream, the 'mode' parameter
  92. is similar to the one for TFileStream. *)
  93. function CreateFileStream(const fileName: string;
  94. mode: Word = fmOpenRead + fmShareDenyNone): TStream;
  95. // Queries is a file stream corresponding to the fileName exists.
  96. function FileStreamExists(const fileName: string): Boolean;
  97. function CreateResourceStream(const ResName: string; ResType: PChar): TGLResourceStream;
  98. function StrToGLSResType(const AStrRes: string): TGLApplicationResource;
  99. var
  100. vAFIOCreateFileStream: TAFIOCreateFileStream = nil;
  101. vAFIOFileStreamExists: TAFIOFileStreamExists = nil;
  102. // ---------------------------------------------------------------------
  103. implementation
  104. // ---------------------------------------------------------------------
  105. var
  106. vAFIO: TGLApplicationFileIO = nil;
  107. function ApplicationFileIODefined: Boolean;
  108. begin
  109. Result := (Assigned(vAFIOCreateFileStream) and Assigned(vAFIOFileStreamExists))
  110. or Assigned(vAFIO);
  111. end;
  112. function CreateFileStream(const fileName: string;
  113. mode: Word = fmOpenRead + fmShareDenyNone): TStream;
  114. begin
  115. if Assigned(vAFIOCreateFileStream) then
  116. Result := vAFIOCreateFileStream(fileName, mode)
  117. else
  118. begin
  119. Result:=nil;
  120. if Assigned(vAFIO) and Assigned(vAFIO.FOnFileStream) then
  121. vAFIO.FOnFileStream(fileName, mode, Result);
  122. if not Assigned(Result) then
  123. begin
  124. if ((mode and fmCreate)=fmCreate) or FileExists(fileName) then
  125. Result := TFileStream.Create(fileName, mode)
  126. else
  127. raise
  128. Exception.Create('File not found: "'+fileName+'"');
  129. end;
  130. end;
  131. end;
  132. function FileStreamExists(const fileName: string): Boolean;
  133. begin
  134. if Assigned(vAFIOFileStreamExists) then
  135. Result := vAFIOFileStreamExists(fileName)
  136. else
  137. begin
  138. if Assigned(vAFIO) and Assigned(vAFIO.FOnFileStreamExists) then
  139. Result := vAFIO.FOnFileStreamExists(fileName)
  140. else
  141. Result := FileExists(fileName);
  142. end;
  143. end;
  144. function CreateResourceStream(const ResName: string; ResType: PChar): TGLResourceStream;
  145. var
  146. InfoBlock: HRSRC;
  147. begin
  148. Result := nil;
  149. InfoBlock := FindResource(HInstance, PChar(ResName), ResType);
  150. if InfoBlock <> 0 then
  151. Result := TResourceStream.Create(HInstance, ResName, ResType)
  152. else
  153. GLSLogger.LogError(Format('Can''t create stream of application resource "%s"', [ResName]));
  154. end;
  155. // ------------------
  156. // ------------------ TGLApplicationFileIO ------------------
  157. // ------------------
  158. constructor TGLApplicationFileIO.Create(AOwner: TComponent);
  159. begin
  160. inherited Create(AOwner);
  161. vAFIO := Self;
  162. end;
  163. destructor TGLApplicationFileIO.Destroy;
  164. begin
  165. vAFIO := nil;
  166. inherited Destroy;
  167. end;
  168. // ------------------
  169. // ------------------ TGLDataFile ------------------
  170. // ------------------
  171. class function TGLDataFile.Capabilities: TGLDataFileCapabilities;
  172. begin
  173. Result := [dfcRead];
  174. end;
  175. function TGLDataFile.CreateCopy(AOwner: TPersistent): TGLDataFile;
  176. begin
  177. if Self <> nil then
  178. Result := TGLDataFileClass(Self.ClassType).Create(AOwner)
  179. else
  180. Result := nil;
  181. end;
  182. procedure TGLDataFile.LoadFromFile(const fileName: string);
  183. var
  184. fs: TStream;
  185. begin
  186. ResourceName := ExtractFileName(fileName);
  187. fs := CreateFileStream(fileName, fmOpenRead + fmShareDenyNone);
  188. try
  189. LoadFromStream(fs);
  190. finally
  191. fs.Free;
  192. end;
  193. end;
  194. procedure TGLDataFile.SaveToFile(const fileName: string);
  195. var
  196. fs: TStream;
  197. begin
  198. ResourceName := ExtractFileName(fileName);
  199. fs := CreateFileStream(fileName, fmCreate);
  200. try
  201. SaveToStream(fs);
  202. finally
  203. fs.Free;
  204. end;
  205. end;
  206. procedure TGLDataFile.LoadFromStream(stream: TStream);
  207. begin
  208. Assert(False, 'Import for ' + ClassName + ' to ' + stream.ClassName + ' not available.');
  209. end;
  210. procedure TGLDataFile.SaveToStream(stream: TStream);
  211. begin
  212. Assert(False, 'Export for ' + ClassName + ' to ' + stream.ClassName + ' not available.');
  213. end;
  214. procedure TGLDataFile.Initialize;
  215. begin
  216. end;
  217. procedure TGLDataFile.SetResourceName(const AName: string);
  218. begin
  219. FResourceName := AName;
  220. end;
  221. function StrToGLSResType(const AStrRes: string): TGLApplicationResource;
  222. begin
  223. if AStrRes = '[SAMPLERS]' then
  224. begin
  225. Result := aresSampler;
  226. end
  227. else if AStrRes = '[TEXTURES]' then
  228. begin
  229. Result := aresTexture;
  230. end
  231. else if AStrRes = '[MATERIALS]' then
  232. begin
  233. Result := aresMaterial;
  234. end
  235. else if AStrRes = '[STATIC MESHES]' then
  236. begin
  237. Result := aresMesh;
  238. end
  239. else if AStrRes = '[SPLASH]' then
  240. begin
  241. Result := aresSplash;
  242. end
  243. else if AStrRes = '[FONTS]' then
  244. begin
  245. Result := aresFont;
  246. end
  247. else
  248. Result := aresNone;
  249. end;
  250. end.