LibHac/NandReaderGui/DeviceStream.cs
2018-07-14 15:48:41 -05:00

163 lines
5.1 KiB
C#

using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace NandReaderGui
{
public class DeviceStream : Stream
{
public const short FileAttributeNormal = 0x80;
public const short InvalidHandleValue = -1;
public const uint GenericRead = 0x80000000;
public const uint GenericWrite = 0x40000000;
public const uint CreateNew = 1;
public const uint CreateAlways = 2;
public const uint OpenExisting = 3;
// Use interop to call the CreateFile function.
// For more information about CreateFile,
// see the unmanaged MSDN reference library.
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadFile(
IntPtr hFile, // handle to file
byte[] lpBuffer, // data buffer
int nNumberOfBytesToRead, // number of bytes to read
ref int lpNumberOfBytesRead, // number of bytes read
IntPtr lpOverlapped
//
// ref OVERLAPPED lpOverlapped // overlapped buffer
);
private SafeFileHandle _handleValue;
private FileStream _fs;
public DeviceStream(string device, long length)
{
Load(device);
Length = length;
}
private void Load(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
// Try to open the file.
IntPtr ptr = CreateFile(path, GenericRead, 0, IntPtr.Zero, OpenExisting, 0, IntPtr.Zero);
_handleValue = new SafeFileHandle(ptr, true);
_fs = new FileStream(_handleValue, FileAccess.Read);
// If the handle is invalid,
// get the last Win32 error
// and throw a Win32Exception.
if (_handleValue.IsInvalid)
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
}
public override bool CanRead { get; } = true;
public override bool CanSeek => true;
public override bool CanWrite => false;
public override void Flush() { }
public override long Length { get; }
public override long Position
{
get => _fs.Position;
set => _fs.Position = value;
}
public override int Read(byte[] buffer, int offset, int count)
{
int bytesRead = 0;
var bufBytes = new byte[count];
if (!ReadFile(_handleValue.DangerousGetHandle(), bufBytes, count, ref bytesRead, IntPtr.Zero))
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
for (int i = 0; i < bytesRead; i++)
{
buffer[offset + i] = bufBytes[i];
}
return bytesRead;
}
public override int ReadByte()
{
int bytesRead = 0;
var lpBuffer = new byte[1];
if (!ReadFile(
_handleValue.DangerousGetHandle(), // handle to file
lpBuffer, // data buffer
1, // number of bytes to read
ref bytesRead, // number of bytes read
IntPtr.Zero
))
{ Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); }
return lpBuffer[0];
}
public override long Seek(long offset, SeekOrigin origin) => _fs.Seek(offset, origin);
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override void Close()
{
_handleValue.Close();
_handleValue.Dispose();
_handleValue = null;
base.Close();
}
private bool _disposed;
private new void Dispose()
{
Dispose(true);
base.Dispose();
GC.SuppressFinalize(this);
}
private new void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!_disposed)
{
if (disposing)
{
if (_handleValue != null)
{
_fs.Dispose();
_handleValue.Close();
_handleValue.Dispose();
_handleValue = null;
}
}
// Note disposing has been done.
_disposed = true;
}
}
}
}