using System;
using System.IO;
using Godot;
namespace TBL.GodotSharp.IO.File
{
///
/// Wrapper for that allows it to be used as a .
///
///
/// Use this if really necessary, but prefer to use .NET's built in IO classes as they will be quite a bit faster.
/// If you need to convert a godot path to an absolute one, use .
///
public class GodotFileStream : Stream
{
private Godot.File _file;
private Godot.File.ModeFlags _flags;
///
/// Create a new instance of the class.
///
/// The path to the file to open. This accepts Godot-style user://
and res://
paths.
/// File flags.
/// If not null, this will enable compression/decompression.
public GodotFileStream(string path, Godot.File.ModeFlags flags,
Godot.File.CompressionMode? compressionMode = null)
{
if (path == null)
throw new ArgumentNullException("path");
_file = new Godot.File();
_flags = flags;
Error result;
if (compressionMode.HasValue)
{
result = _file.OpenCompressed(path, flags, compressionMode.Value);
}
else
{
result = _file.Open(path, flags);
}
if (result != Error.Ok)
{
throw new IOException($"Unable to open \"{path}\": {result}");
}
}
public override bool CanRead => _flags == Godot.File.ModeFlags.Read ||
_flags == Godot.File.ModeFlags.ReadWrite ||
_flags == Godot.File.ModeFlags.WriteRead;
public override bool CanSeek => true;
public override bool CanWrite => _flags == Godot.File.ModeFlags.Write ||
_flags == Godot.File.ModeFlags.ReadWrite ||
_flags == Godot.File.ModeFlags.WriteRead;
public override long Length => checked((long)_file.GetLen());
public override long Position
{
get => checked((long)_file.GetPosition());
set => _file.Seek(value);
}
public override void Flush()
{
_file.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
if (!_file.IsOpen())
throw new ObjectDisposedException("File has been closed");
if (!CanRead)
throw new NotSupportedException($"Cannot Read on a GodotFileStream with flags {_flags}");
if (count < 0)
throw new ArgumentOutOfRangeException("count");
if (offset < 0)
throw new ArgumentOutOfRangeException("offset");
if (count + offset > buffer.Length)
throw new ArgumentException("count + offset is greater than the given buffer length");
var data = _file.GetBuffer(count);
if (_file.GetError() != Error.Ok)
throw new IOException($"Error reading file: {_file.GetError()}");
Array.Copy(data, 0, buffer, offset, data.Length);
return data.Length;
}
public override long Seek(long offset, SeekOrigin origin)
{
if (!_file.IsOpen())
throw new ObjectDisposedException("File has been closed");
switch (origin)
{
case SeekOrigin.Begin:
_file.Seek(offset);
break;
case SeekOrigin.Current:
_file.Seek(checked((long)_file.GetPosition()) + offset);
break;
case SeekOrigin.End:
_file.SeekEnd(offset);
break;
}
if (_file.GetError() != Error.Ok)
throw new IOException($"Error seeking file: {_file.GetError()}");
return checked((long)_file.GetPosition());
}
public override void SetLength(long value)
{
throw new NotSupportedException("GodotFileStream does not support SetLength");
}
public override void Write(byte[] buffer, int offset, int count)
{
if (!_file.IsOpen())
throw new ObjectDisposedException("File has been closed");
if (!CanWrite)
throw new NotSupportedException($"Cannot Write on a GodotFileStream with flags {_flags}");
if (count < 0)
throw new ArgumentOutOfRangeException("count");
if (offset < 0)
throw new ArgumentOutOfRangeException("offset");
if (count + offset > buffer.Length)
throw new ArgumentException("count + offset is greater than the given buffer length");
var data = new byte[count];
Array.Copy(buffer, offset, data, 0, count);
_file.StoreBuffer(data);
if (_file.GetError() != Error.Ok)
throw new IOException($"Error writing file: {_file.GetError()}");
}
protected override void Dispose(bool disposing)
{
_file.Close();
}
}
}