GXS.ApplicationFileIO.pas 7.7 KB

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