mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add FS access log
This commit is contained in:
parent
5d32150ad8
commit
1859b4ce26
8 changed files with 328 additions and 31 deletions
|
@ -11,6 +11,8 @@ namespace LibHac.Fs.Accessors
|
|||
Directory = directory;
|
||||
}
|
||||
|
||||
public int GetId() => Directory.GetHashCode();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Directory.Dispose();
|
||||
|
|
|
@ -11,6 +11,8 @@ namespace LibHac.Fs.Accessors
|
|||
File = file;
|
||||
}
|
||||
|
||||
public int GetId() => File.GetHashCode();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
File.Dispose();
|
||||
|
|
|
@ -11,28 +11,20 @@ namespace LibHac.Fs.Accessors
|
|||
public string Name { get; }
|
||||
|
||||
private IFileSystem FileSystem { get; }
|
||||
private IAccessLogger Logger { get; }
|
||||
private ITimeSpanGenerator Timer { get; }
|
||||
|
||||
private HashSet<FileAccessor> OpenFiles { get; } = new HashSet<FileAccessor>();
|
||||
private HashSet<DirectoryAccessor> OpenDirectories { get; } = new HashSet<DirectoryAccessor>();
|
||||
|
||||
private readonly object _locker = new object();
|
||||
|
||||
internal bool IsAccessLogEnabled { get; set; }
|
||||
|
||||
public FileSystemAccessor(string name, IFileSystem baseFileSystem)
|
||||
{
|
||||
Name = name;
|
||||
FileSystem = baseFileSystem;
|
||||
}
|
||||
|
||||
public FileSystemAccessor(string name, IFileSystem baseFileSystem, IAccessLogger logger, ITimeSpanGenerator timer)
|
||||
{
|
||||
Name = name;
|
||||
FileSystem = baseFileSystem;
|
||||
Logger = logger;
|
||||
Timer = timer;
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path)
|
||||
{
|
||||
FileSystem.CreateDirectory(path);
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
namespace LibHac.Fs.Accessors
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace LibHac.Fs.Accessors
|
||||
{
|
||||
public interface IAccessLogger
|
||||
{
|
||||
|
||||
void Log(TimeSpan startTime, TimeSpan endTime, int handleId, string message, [CallerMemberName] string caller = "");
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Fs.Accessors;
|
||||
|
||||
using static LibHac.Results;
|
||||
|
@ -10,19 +11,32 @@ namespace LibHac.Fs
|
|||
public class FileSystemManager
|
||||
{
|
||||
internal Horizon Os { get; }
|
||||
internal ITimeSpanGenerator Time { get; }
|
||||
internal IAccessLogger Logger { get; }
|
||||
|
||||
internal MountTable MountTable { get; } = new MountTable();
|
||||
|
||||
private bool AccessLogEnabled { get; set; } = true;
|
||||
|
||||
public FileSystemManager(Horizon os)
|
||||
{
|
||||
Os = os;
|
||||
}
|
||||
|
||||
public FileSystemManager(Horizon os, IAccessLogger logger, ITimeSpanGenerator timer)
|
||||
{
|
||||
Os = os;
|
||||
Logger = logger;
|
||||
Time = timer;
|
||||
}
|
||||
|
||||
public void Register(string mountName, IFileSystem fileSystem)
|
||||
{
|
||||
var accessor = new FileSystemAccessor(mountName, fileSystem);
|
||||
|
||||
MountTable.Mount(accessor);
|
||||
|
||||
accessor.IsAccessLogEnabled = IsEnabledAccessLog();
|
||||
}
|
||||
|
||||
public void CreateDirectory(string path)
|
||||
|
@ -30,7 +44,18 @@ namespace LibHac.Fs
|
|||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
|
||||
fileSystem.CreateDirectory(subPath.ToString());
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
fileSystem.CreateDirectory(subPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{path}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem.CreateDirectory(subPath.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateFile(string path, long size)
|
||||
|
@ -43,7 +68,18 @@ namespace LibHac.Fs
|
|||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
|
||||
fileSystem.CreateFile(subPath.ToString(), size, options);
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
fileSystem.CreateFile(subPath.ToString(), size, options);
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{path}\", size: {size}");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem.CreateFile(subPath.ToString(), size, options);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path)
|
||||
|
@ -51,7 +87,18 @@ namespace LibHac.Fs
|
|||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
|
||||
fileSystem.DeleteDirectory(subPath.ToString());
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
fileSystem.DeleteDirectory(subPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{path}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem.DeleteDirectory(subPath.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteDirectoryRecursively(string path)
|
||||
|
@ -59,7 +106,18 @@ namespace LibHac.Fs
|
|||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
|
||||
fileSystem.DeleteDirectoryRecursively(subPath.ToString());
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
fileSystem.DeleteDirectoryRecursively(subPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{path}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem.DeleteDirectoryRecursively(subPath.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void CleanDirectoryRecursively(string path)
|
||||
|
@ -67,7 +125,18 @@ namespace LibHac.Fs
|
|||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
|
||||
fileSystem.CleanDirectoryRecursively(subPath.ToString());
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
fileSystem.CleanDirectoryRecursively(subPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{path}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem.CleanDirectoryRecursively(subPath.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteFile(string path)
|
||||
|
@ -75,7 +144,18 @@ namespace LibHac.Fs
|
|||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
|
||||
fileSystem.DeleteFile(subPath.ToString());
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
fileSystem.DeleteFile(subPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{path}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem.DeleteFile(subPath.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void RenameDirectory(string oldPath, string newPath)
|
||||
|
@ -91,7 +171,18 @@ namespace LibHac.Fs
|
|||
ThrowHelper.ThrowResult(ResultFsDifferentDestFileSystem);
|
||||
}
|
||||
|
||||
oldFileSystem.RenameDirectory(oldSubPath.ToString(), newSubPath.ToString());
|
||||
if (IsEnabledAccessLog() && oldFileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
oldFileSystem.RenameDirectory(oldSubPath.ToString(), newSubPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{oldPath}\", new_path: \"{newPath}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
oldFileSystem.RenameDirectory(oldSubPath.ToString(), newSubPath.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void RenameFile(string oldPath, string newPath)
|
||||
|
@ -107,7 +198,18 @@ namespace LibHac.Fs
|
|||
ThrowHelper.ThrowResult(ResultFsDifferentDestFileSystem);
|
||||
}
|
||||
|
||||
oldFileSystem.RenameFile(oldSubPath.ToString(), newSubPath.ToString());
|
||||
if (IsEnabledAccessLog() && oldFileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
oldFileSystem.RenameFile(oldSubPath.ToString(), newSubPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{oldPath}\", new_path: \"{newPath}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
oldFileSystem.RenameFile(oldSubPath.ToString(), newSubPath.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public DirectoryEntryType GetEntryType(string path)
|
||||
|
@ -115,7 +217,22 @@ namespace LibHac.Fs
|
|||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
|
||||
return fileSystem.GetEntryType(subPath.ToString());
|
||||
DirectoryEntryType type;
|
||||
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
type = fileSystem.GetEntryType(subPath.ToString());
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", path: \"{path}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
type = fileSystem.GetEntryType(subPath.ToString());
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
public FileHandle OpenFile(string path, OpenMode mode)
|
||||
|
@ -123,9 +240,24 @@ namespace LibHac.Fs
|
|||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
|
||||
FileAccessor file = fileSystem.OpenFile(subPath.ToString(), mode);
|
||||
FileHandle handle;
|
||||
|
||||
return new FileHandle(file);
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
FileAccessor file = fileSystem.OpenFile(subPath.ToString(), mode);
|
||||
handle = new FileHandle(file);
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, handle, $", path: \"{path}\", open_mode: {mode}");
|
||||
}
|
||||
else
|
||||
{
|
||||
FileAccessor file = fileSystem.OpenFile(subPath.ToString(), mode);
|
||||
handle = new FileHandle(file);
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
public DirectoryHandle OpenDirectory(string path, OpenDirectoryMode mode)
|
||||
|
@ -133,9 +265,24 @@ namespace LibHac.Fs
|
|||
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||
.ThrowIfFailure();
|
||||
|
||||
DirectoryAccessor dir = fileSystem.OpenDirectory(subPath.ToString(), mode);
|
||||
DirectoryHandle handle;
|
||||
|
||||
return new DirectoryHandle(dir);
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
DirectoryAccessor dir = fileSystem.OpenDirectory(subPath.ToString(), mode);
|
||||
handle = new DirectoryHandle(dir);
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, handle, $", path: \"{path}\", open_mode: {mode}");
|
||||
}
|
||||
else
|
||||
{
|
||||
DirectoryAccessor dir = fileSystem.OpenDirectory(subPath.ToString(), mode);
|
||||
handle = new DirectoryHandle(dir);
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
public long GetFreeSpaceSize(string path)
|
||||
|
@ -166,7 +313,18 @@ namespace LibHac.Fs
|
|||
{
|
||||
MountTable.Find(mountName, out FileSystemAccessor fileSystem).ThrowIfFailure();
|
||||
|
||||
fileSystem.Commit();
|
||||
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
fileSystem.Commit();
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, $", name: \"{mountName}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystem.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================
|
||||
|
@ -179,7 +337,22 @@ namespace LibHac.Fs
|
|||
|
||||
public int ReadFile(FileHandle handle, Span<byte> destination, long offset, ReadOption option)
|
||||
{
|
||||
return handle.File.Read(destination, offset, option);
|
||||
int bytesRead;
|
||||
|
||||
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
bytesRead = handle.File.Read(destination, offset, option);
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, handle, $", offset: {offset}, size: {destination.Length}");
|
||||
}
|
||||
else
|
||||
{
|
||||
bytesRead = handle.File.Read(destination, offset, option);
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public void WriteFile(FileHandle handle, ReadOnlySpan<byte> source, long offset)
|
||||
|
@ -189,12 +362,36 @@ namespace LibHac.Fs
|
|||
|
||||
public void WriteFile(FileHandle handle, ReadOnlySpan<byte> source, long offset, WriteOption option)
|
||||
{
|
||||
handle.File.Write(source, offset, option);
|
||||
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
handle.File.Write(source, offset, option);
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
string optionString = (option & WriteOption.Flush) == 0 ? "" : $", write_option: {option}";
|
||||
|
||||
OutputAccessLog(startTime, endTime, handle, $", offset: {offset}, size: {source.Length}{optionString}");
|
||||
}
|
||||
else
|
||||
{
|
||||
handle.File.Write(source, offset, option);
|
||||
}
|
||||
}
|
||||
|
||||
public void FlushFile(FileHandle handle)
|
||||
{
|
||||
handle.File.Flush();
|
||||
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
handle.File.Flush();
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, handle, string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
handle.File.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
public long GetFileSize(FileHandle handle)
|
||||
|
@ -204,7 +401,18 @@ namespace LibHac.Fs
|
|||
|
||||
public void SetFileSize(FileHandle handle, long size)
|
||||
{
|
||||
handle.File.SetSize(size);
|
||||
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
handle.File.SetSize(size);
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, handle, $", size: {size}");
|
||||
}
|
||||
else
|
||||
{
|
||||
handle.File.SetSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
public OpenMode GetFileOpenMode(FileHandle handle)
|
||||
|
@ -214,7 +422,18 @@ namespace LibHac.Fs
|
|||
|
||||
public void CloseFile(FileHandle handle)
|
||||
{
|
||||
handle.File.Dispose();
|
||||
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
handle.Dispose();
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, handle, string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
handle.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================
|
||||
|
@ -227,6 +446,16 @@ namespace LibHac.Fs
|
|||
|
||||
public IEnumerable<DirectoryEntry> ReadDirectory(DirectoryHandle handle)
|
||||
{
|
||||
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
IEnumerable<DirectoryEntry> entries = handle.Directory.Read();
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(startTime, endTime, handle, string.Empty);
|
||||
return entries;
|
||||
}
|
||||
|
||||
return handle.Directory.Read();
|
||||
}
|
||||
|
||||
|
@ -278,5 +507,35 @@ namespace LibHac.Fs
|
|||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
internal bool IsEnabledAccessLog()
|
||||
{
|
||||
return AccessLogEnabled && Logger != null && Time != null;
|
||||
}
|
||||
|
||||
internal bool IsEnabledHandleAccessLog(FileHandle handle)
|
||||
{
|
||||
return handle.File.Parent.IsAccessLogEnabled;
|
||||
}
|
||||
|
||||
internal bool IsEnabledHandleAccessLog(DirectoryHandle handle)
|
||||
{
|
||||
return handle.Directory.Parent.IsAccessLogEnabled;
|
||||
}
|
||||
|
||||
internal void OutputAccessLog(TimeSpan startTime, TimeSpan endTime, string message, [CallerMemberName] string caller = "")
|
||||
{
|
||||
Logger.Log(startTime, endTime, 0, message, caller);
|
||||
}
|
||||
|
||||
internal void OutputAccessLog(TimeSpan startTime, TimeSpan endTime, FileHandle handle, string message, [CallerMemberName] string caller = "")
|
||||
{
|
||||
Logger.Log(startTime, endTime, handle.GetId(), message, caller);
|
||||
}
|
||||
|
||||
internal void OutputAccessLog(TimeSpan startTime, TimeSpan endTime, DirectoryHandle handle, string message, [CallerMemberName] string caller = "")
|
||||
{
|
||||
Logger.Log(startTime, endTime, handle.GetId(), message, caller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using LibHac.Fs;
|
||||
using LibHac.Fs.Accessors;
|
||||
|
||||
namespace LibHac
|
||||
{
|
||||
|
@ -12,5 +13,12 @@ namespace LibHac
|
|||
{
|
||||
Fs = new FileSystemManager(this);
|
||||
}
|
||||
|
||||
public Horizon(IAccessLogger logger, ITimeSpanGenerator timer)
|
||||
{
|
||||
Time = timer;
|
||||
|
||||
Fs = new FileSystemManager(this, logger, timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
15
src/hactoolnet/ConsoleAccessLog.cs
Normal file
15
src/hactoolnet/ConsoleAccessLog.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Fs.Accessors;
|
||||
|
||||
namespace hactoolnet
|
||||
{
|
||||
public class ConsoleAccessLog : IAccessLogger
|
||||
{
|
||||
public void Log(TimeSpan startTime, TimeSpan endTime, int handleId, string message, [CallerMemberName] string caller = "")
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"FS_ACCESS: {{ start: {startTime.Milliseconds,9}, end: {endTime.Milliseconds,9}, handle: 0x{handleId:x8}, function: \"{caller}\"{message} }}");
|
||||
}
|
||||
}
|
||||
}
|
16
src/hactoolnet/TimeSpanTimer.cs
Normal file
16
src/hactoolnet/TimeSpanTimer.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using LibHac;
|
||||
|
||||
namespace hactoolnet
|
||||
{
|
||||
public class TimeSpanTimer : ITimeSpanGenerator
|
||||
{
|
||||
private Stopwatch Timer = Stopwatch.StartNew();
|
||||
|
||||
public TimeSpan GetCurrent()
|
||||
{
|
||||
return Timer.Elapsed;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue