GodotFileStream.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. using System;
  2. using System.IO;
  3. using Godot;
  4. namespace TBL.GodotSharp.IO.File
  5. {
  6. /// <summary>
  7. /// Wrapper for <see cref="Godot.File"/> that allows it to be used as a <see cref="System.IO.Stream"/>.
  8. /// </summary>
  9. /// <remarks>
  10. /// Use this if really necessary, but prefer to use .NET's built in IO classes as they will be quite a bit faster.
  11. /// If you need to convert a godot path to an absolute one, use <see cref="ProjectSettings.GlobalizePath(string)"/>.
  12. /// </remarks>
  13. public class GodotFileStream : Stream
  14. {
  15. private Godot.File _file;
  16. private Godot.File.ModeFlags _flags;
  17. /// <summary>
  18. /// Create a new instance of the <see cref="GodotFileStream"/> class.
  19. /// </summary>
  20. /// <param name="path">The path to the file to open. This accepts Godot-style <code>user://</code> and <code>res://</code> paths.</param>
  21. /// <param name="flags">File flags.</param>
  22. /// <param name="compressionMode">If not null, this will enable compression/decompression.</param>
  23. public GodotFileStream(string path, Godot.File.ModeFlags flags,
  24. Godot.File.CompressionMode? compressionMode = null)
  25. {
  26. if (path == null)
  27. throw new ArgumentNullException("path");
  28. _file = new Godot.File();
  29. _flags = flags;
  30. Error result;
  31. if (compressionMode.HasValue)
  32. {
  33. result = _file.OpenCompressed(path, flags, compressionMode.Value);
  34. }
  35. else
  36. {
  37. result = _file.Open(path, flags);
  38. }
  39. if (result != Error.Ok)
  40. {
  41. throw new IOException($"Unable to open \"{path}\": {result}");
  42. }
  43. }
  44. public override bool CanRead => _flags == Godot.File.ModeFlags.Read ||
  45. _flags == Godot.File.ModeFlags.ReadWrite ||
  46. _flags == Godot.File.ModeFlags.WriteRead;
  47. public override bool CanSeek => true;
  48. public override bool CanWrite => _flags == Godot.File.ModeFlags.Write ||
  49. _flags == Godot.File.ModeFlags.ReadWrite ||
  50. _flags == Godot.File.ModeFlags.WriteRead;
  51. public override long Length => checked((long)_file.GetLen());
  52. public override long Position
  53. {
  54. get => checked((long)_file.GetPosition());
  55. set => _file.Seek(value);
  56. }
  57. public override void Flush()
  58. {
  59. _file.Flush();
  60. }
  61. public override int Read(byte[] buffer, int offset, int count)
  62. {
  63. if (!_file.IsOpen())
  64. throw new ObjectDisposedException("File has been closed");
  65. if (!CanRead)
  66. throw new NotSupportedException($"Cannot Read on a GodotFileStream with flags {_flags}");
  67. if (count < 0)
  68. throw new ArgumentOutOfRangeException("count");
  69. if (offset < 0)
  70. throw new ArgumentOutOfRangeException("offset");
  71. if (count + offset > buffer.Length)
  72. throw new ArgumentException("count + offset is greater than the given buffer length");
  73. var data = _file.GetBuffer(count);
  74. if (_file.GetError() != Error.Ok)
  75. throw new IOException($"Error reading file: {_file.GetError()}");
  76. Array.Copy(data, 0, buffer, offset, data.Length);
  77. return data.Length;
  78. }
  79. public override long Seek(long offset, SeekOrigin origin)
  80. {
  81. if (!_file.IsOpen())
  82. throw new ObjectDisposedException("File has been closed");
  83. switch (origin)
  84. {
  85. case SeekOrigin.Begin:
  86. _file.Seek(offset);
  87. break;
  88. case SeekOrigin.Current:
  89. _file.Seek(checked((long)_file.GetPosition()) + offset);
  90. break;
  91. case SeekOrigin.End:
  92. _file.SeekEnd(offset);
  93. break;
  94. }
  95. if (_file.GetError() != Error.Ok)
  96. throw new IOException($"Error seeking file: {_file.GetError()}");
  97. return checked((long)_file.GetPosition());
  98. }
  99. public override void SetLength(long value)
  100. {
  101. throw new NotSupportedException("GodotFileStream does not support SetLength");
  102. }
  103. public override void Write(byte[] buffer, int offset, int count)
  104. {
  105. if (!_file.IsOpen())
  106. throw new ObjectDisposedException("File has been closed");
  107. if (!CanWrite)
  108. throw new NotSupportedException($"Cannot Write on a GodotFileStream with flags {_flags}");
  109. if (count < 0)
  110. throw new ArgumentOutOfRangeException("count");
  111. if (offset < 0)
  112. throw new ArgumentOutOfRangeException("offset");
  113. if (count + offset > buffer.Length)
  114. throw new ArgumentException("count + offset is greater than the given buffer length");
  115. var data = new byte[count];
  116. Array.Copy(buffer, offset, data, 0, count);
  117. _file.StoreBuffer(data);
  118. if (_file.GetError() != Error.Ok)
  119. throw new IOException($"Error writing file: {_file.GetError()}");
  120. }
  121. protected override void Dispose(bool disposing)
  122. {
  123. _file.Close();
  124. }
  125. }
  126. }