Add IResultLogger and IResultNameResolver

This commit is contained in:
Alex Barney 2020-01-24 15:11:31 -07:00
parent 5910b9f9c2
commit 0040db1153
7 changed files with 365 additions and 175 deletions

View File

@ -10,8 +10,8 @@ namespace LibHac.Fs
public static Result.Base PathAlreadyExists => new Result.Base(ModuleFs, 2);
public static Result.Base TargetLocked => new Result.Base(ModuleFs, 7);
public static Result.Base DirectoryNotEmpty => new Result.Base(ModuleFs, 8);
public static Result.Base InsufficientFreeSpace => new Result.Base(ResultFs.ModuleFs, 30, 45);
public static Result.Base InsufficientFreeSpaceBis => new Result.Base(ResultFs.ModuleFs, 34, 38);
public static Result.Base InsufficientFreeSpace => new Result.Base(ModuleFs, 30, 45);
public static Result.Base InsufficientFreeSpaceBis => new Result.Base(ModuleFs, 34, 38);
public static Result.Base InsufficientFreeSpaceBisCalibration => new Result.Base(ModuleFs, 35);
public static Result.Base InsufficientFreeSpaceBisSafe => new Result.Base(ModuleFs, 36);
public static Result.Base InsufficientFreeSpaceBisUser => new Result.Base(ModuleFs, 37);
@ -23,10 +23,10 @@ namespace LibHac.Fs
public static Result.Base TargetNotFound => new Result.Base(ModuleFs, 1002);
public static Result.Base ExternalKeyNotFound => new Result.Base(ModuleFs, 1004);
public static Result.Base SdCardAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 2000, 2499); }
public static Result.Base SdCardAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 2000, 2499); }
public static Result.Base SdCardNotPresent => new Result.Base(ModuleFs, 2001);
public static Result.Base GameCardAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 2500, 2999); }
public static Result.Base GameCardAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 2500, 2999); }
public static Result.Base InvalidBufferForGameCard => new Result.Base(ModuleFs, 2503);
public static Result.Base GameCardNotInserted => new Result.Base(ModuleFs, 2520);
@ -41,20 +41,20 @@ namespace LibHac.Fs
public static Result.Base SaveDataPathAlreadyExists => new Result.Base(ModuleFs, 3003);
public static Result.Base OutOfRange => new Result.Base(ModuleFs, 3005);
public static Result.Base AllocationMemoryFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 3200, 3499); }
public static Result.Base AllocationMemoryFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 3200, 3499); }
public static Result.Base AesXtsFileFileStorageAllocationError => new Result.Base(ModuleFs, 3312);
public static Result.Base AesXtsFileXtsStorageAllocationError => new Result.Base(ModuleFs, 3313);
public static Result.Base AesXtsFileAlignmentStorageAllocationError => new Result.Base(ModuleFs, 3314);
public static Result.Base AesXtsFileStorageFileAllocationError => new Result.Base(ModuleFs, 3315);
public static Result.Base AesXtsFileSubStorageAllocationError => new Result.Base(ModuleFs, 3383);
public static Result.Base MmcAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 3500, 3999); }
public static Result.Base MmcAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 3500, 3999); }
public static Result.Base DataCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 4000, 4999); }
public static Result.Base RomCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 4001, 4299); }
public static Result.Base DataCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4000, 4999); }
public static Result.Base RomCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4001, 4299); }
public static Result.Base InvalidIndirectStorageSource => new Result.Base(ModuleFs, 4023);
public static Result.Base SaveDataCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 4301, 4499); }
public static Result.Base SaveDataCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4301, 4499); }
public static Result.Base Result4302 => new Result.Base(ModuleFs, 4302);
public static Result.Base InvalidSaveDataEntryType => new Result.Base(ModuleFs, 4303);
public static Result.Base InvalidSaveDataHeader => new Result.Base(ModuleFs, 4315);
@ -70,34 +70,34 @@ namespace LibHac.Fs
public static Result.Base SaveDataFileTableCorrupted => new Result.Base(ModuleFs, 4463);
public static Result.Base AllocationTableIteratedRangeEntry => new Result.Base(ModuleFs, 4464);
public static Result.Base NcaCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 4501, 4599); }
public static Result.Base NcaCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4501, 4599); }
public static Result.Base IntegrityVerificationStorageCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 4601, 4639); }
public static Result.Base IntegrityVerificationStorageCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4601, 4639); }
public static Result.Base Result4602 => new Result.Base(ModuleFs, 4602);
public static Result.Base Result4603 => new Result.Base(ModuleFs, 4603);
public static Result.Base InvalidHashInIvfc => new Result.Base(ModuleFs, 4604);
public static Result.Base IvfcHashIsEmpty => new Result.Base(ModuleFs, 4612);
public static Result.Base InvalidHashInIvfcTopLayer => new Result.Base(ModuleFs, 4613);
public static Result.Base PartitionFileSystemCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 4641, 4659); }
public static Result.Base PartitionFileSystemCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4641, 4659); }
public static Result.Base InvalidPartitionFileSystemHashOffset => new Result.Base(ModuleFs, 4642);
public static Result.Base InvalidPartitionFileSystemHash => new Result.Base(ModuleFs, 4643);
public static Result.Base InvalidPartitionFileSystemMagic => new Result.Base(ModuleFs, 4644);
public static Result.Base InvalidHashedPartitionFileSystemMagic => new Result.Base(ModuleFs, 4645);
public static Result.Base InvalidPartitionFileSystemEntryNameOffset => new Result.Base(ModuleFs, 4646);
public static Result.Base BuiltInStorageCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 4661, 4679); }
public static Result.Base BuiltInStorageCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4661, 4679); }
public static Result.Base Result4662 => new Result.Base(ModuleFs, 4662);
public static Result.Base FatFileSystemCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 4681, 4699); }
public static Result.Base HostFileSystemCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 4701, 4719); }
public static Result.Base FatFileSystemCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4681, 4699); }
public static Result.Base HostFileSystemCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4701, 4719); }
public static Result.Base DatabaseCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 4721, 4739); }
public static Result.Base DatabaseCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4721, 4739); }
public static Result.Base SaveDataAllocationTableCorruptedInternal => new Result.Base(ModuleFs, 4722);
public static Result.Base SaveDataFileTableCorruptedInternal => new Result.Base(ModuleFs, 4723);
public static Result.Base AllocationTableIteratedRangeEntryInternal => new Result.Base(ModuleFs, 4724);
public static Result.Base AesXtsFileSystemCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 4741, 4759); }
public static Result.Base AesXtsFileSystemCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4741, 4759); }
public static Result.Base AesXtsFileHeaderTooShort => new Result.Base(ModuleFs, 4742);
public static Result.Base AesXtsFileHeaderInvalidKeys => new Result.Base(ModuleFs, 4743);
public static Result.Base AesXtsFileHeaderInvalidMagic => new Result.Base(ModuleFs, 4744);
@ -106,15 +106,15 @@ namespace LibHac.Fs
public static Result.Base AesXtsFileHeaderInvalidKeysInRenameFile => new Result.Base(ModuleFs, 4747);
public static Result.Base AesXtsFileHeaderInvalidKeysInSetSize => new Result.Base(ModuleFs, 4748);
public static Result.Base SaveDataTransferDataCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 4761, 4769); }
public static Result.Base SignedSystemPartitionDataCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 4771, 4779); }
public static Result.Base SaveDataTransferDataCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4761, 4769); }
public static Result.Base SignedSystemPartitionDataCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4771, 4779); }
public static Result.Base GameCardLogoDataCorrupted => new Result.Base(ModuleFs, 4781);
public static Result.Base Range4811To4819 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 4811, 4819); }
public static Result.Base Range4811To4819 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4811, 4819); }
public static Result.Base Result4812 => new Result.Base(ModuleFs, 4812);
public static Result.Base Unexpected { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 5000, 5999); }
public static Result.Base Unexpected { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 5000, 5999); }
public static Result.Base UnexpectedErrorInHostFileFlush => new Result.Base(ModuleFs, 5307);
public static Result.Base UnexpectedErrorInHostFileGetSize => new Result.Base(ModuleFs, 5308);
@ -131,7 +131,7 @@ namespace LibHac.Fs
public static Result.Base DirectoryUnobtainable => new Result.Base(ModuleFs, 6006);
public static Result.Base NotNormalized => new Result.Base(ModuleFs, 6007);
public static Result.Base InvalidPathForOperation { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 6030, 6059); }
public static Result.Base InvalidPathForOperation { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6030, 6059); }
public static Result.Base DirectoryNotDeletable => new Result.Base(ModuleFs, 6031);
public static Result.Base DestinationIsSubPathOfSource => new Result.Base(ModuleFs, 6032);
public static Result.Base PathNotFoundInSaveDataFileTable => new Result.Base(ModuleFs, 6033);
@ -145,16 +145,16 @@ namespace LibHac.Fs
public static Result.Base ExtensionSizeInvalid => new Result.Base(ModuleFs, 6067);
public static Result.Base ReadOldSaveDataInfoReader => new Result.Base(ModuleFs, 6068);
public static Result.Base InvalidEnumValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 6080, 6099); }
public static Result.Base InvalidEnumValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6080, 6099); }
public static Result.Base InvalidSaveDataState => new Result.Base(ModuleFs, 6081);
public static Result.Base InvalidSaveDataSpaceId => new Result.Base(ModuleFs, 6082);
public static Result.Base InvalidOperationForOpenMode { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 6200, 6299); }
public static Result.Base InvalidOperationForOpenMode { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6200, 6299); }
public static Result.Base FileExtensionWithoutOpenModeAllowAppend => new Result.Base(ModuleFs, 6201);
public static Result.Base InvalidOpenModeForRead => new Result.Base(ModuleFs, 6202);
public static Result.Base InvalidOpenModeForWrite => new Result.Base(ModuleFs, 6203);
public static Result.Base UnsupportedOperation { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 6300, 6399); }
public static Result.Base UnsupportedOperation { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6300, 6399); }
public static Result.Base SubStorageNotResizable => new Result.Base(ModuleFs, 6302);
public static Result.Base SubStorageNotResizableMiddleOfFile => new Result.Base(ModuleFs, 6303);
public static Result.Base UnsupportedOperationInMemoryStorageSetSize => new Result.Base(ModuleFs, 6304);
@ -176,7 +176,7 @@ namespace LibHac.Fs
public static Result.Base UnsupportedOperationInPartitionFileSetSize => new Result.Base(ModuleFs, 6376);
public static Result.Base UnsupportedOperationIdInPartitionFileSystem => new Result.Base(ModuleFs, 6377);
public static Result.Base PermissionDenied { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 6400, 6449); }
public static Result.Base PermissionDenied { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6400, 6449); }
public static Result.Base ExternalKeyAlreadyRegistered => new Result.Base(ModuleFs, 6452);
public static Result.Base WriteStateUnflushed => new Result.Base(ModuleFs, 6454);
@ -184,17 +184,17 @@ namespace LibHac.Fs
public static Result.Base AllocatorAlignmentViolation => new Result.Base(ModuleFs, 6461);
public static Result.Base UserNotExist => new Result.Base(ModuleFs, 6465);
public static Result.Base EntryNotFound { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 6600, 6699); }
public static Result.Base EntryNotFound { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6600, 6699); }
public static Result.Base OutOfResource { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 6700, 6799); }
public static Result.Base OutOfResource { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6700, 6799); }
public static Result.Base MappingTableFull => new Result.Base(ModuleFs, 6706);
public static Result.Base AllocationTableInsufficientFreeBlocks => new Result.Base(ModuleFs, 6707);
public static Result.Base OpenCountLimit => new Result.Base(ModuleFs, 6709);
public static Result.Base MappingFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 6800, 6899); }
public static Result.Base MappingFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6800, 6899); }
public static Result.Base RemapStorageMapFull => new Result.Base(ModuleFs, 6811);
public static Result.Base BadState { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ResultFs.ModuleFs, 6900, 6999); }
public static Result.Base BadState { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6900, 6999); }
public static Result.Base SubStorageNotInitialized => new Result.Base(ModuleFs, 6902);
public static Result.Base NotMounted => new Result.Base(ModuleFs, 6905);
public static Result.Base SaveDataIsExtending => new Result.Base(ModuleFs, 6906);

View File

@ -4,10 +4,10 @@
{
public const int ModuleKvdb = 20;
public static Result TooLargeKeyOrDbFull => new Result(ModuleKvdb, 1);
public static Result KeyNotFound => new Result(ModuleKvdb, 2);
public static Result AllocationFailed => new Result(ModuleKvdb, 4);
public static Result InvalidKeyValue => new Result(ModuleKvdb, 5);
public static Result BufferInsufficient => new Result(ModuleKvdb, 6);
public static Result.Base TooLargeKeyOrDbFull => new Result.Base(ModuleKvdb, 1);
public static Result.Base KeyNotFound => new Result.Base(ModuleKvdb, 2);
public static Result.Base AllocationFailed => new Result.Base(ModuleKvdb, 4);
public static Result.Base InvalidKeyValue => new Result.Base(ModuleKvdb, 5);
public static Result.Base BufferInsufficient => new Result.Base(ModuleKvdb, 6);
}
}

View File

@ -11,6 +11,10 @@ namespace LibHac
public struct Result : IEquatable<Result>
{
private const BaseType SuccessValue = default;
public static Result Success => new Result(SuccessValue);
private static IResultLogger Logger { get; set; }
private static IResultNameResolver NameResolver { get; set; }
private const int ModuleBitsOffset = 0;
private const int ModuleBitsCount = 9;
@ -27,8 +31,6 @@ namespace LibHac
private readonly BaseType _value;
public static Result Success => new Result(SuccessValue);
public Result(BaseType value)
{
_value = GetBitsValue(value, ModuleBitsOffset, ModuleBitsCount + DescriptionBitsCount);
@ -53,18 +55,6 @@ namespace LibHac
public bool IsSuccess() => _value == SuccessValue;
public bool IsFailure() => !IsSuccess();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static BaseType GetBitsValue(BaseType value, int bitsOffset, int bitsCount)
{
return (value >> bitsOffset) & ~(~default(BaseType) << bitsCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static BaseType SetBitsValue(int value, int bitsOffset, int bitsCount)
{
return ((uint)value & ~(~default(BaseType) << bitsCount)) << bitsOffset;
}
public void ThrowIfFailure()
{
if (IsFailure())
@ -74,8 +64,10 @@ namespace LibHac
}
/// <summary>
/// A function that can contain code for logging or debugging returned results.
/// Intended to be used when returning a non-zero Result:
/// Performs no action in release mode.
/// In debug mode, logs returned results using the <see cref="IResultLogger"/> set by <see cref="SetLogger"/>.
/// <br/>Intended to always be used when returning a non-zero <see cref="Result"/>.
/// <br/><br/>Example:
/// <code>return result.Log();</code>
/// </summary>
/// <returns>The called <see cref="Result"/> value.</returns>
@ -87,7 +79,7 @@ namespace LibHac
}
/// <summary>
/// Same as <see cref="Log"/>, but for when one result is converted to another.
/// In debug mode, logs converted results using the <see cref="IResultLogger"/> set by <see cref="SetLogger"/>.
/// </summary>
/// <param name="originalResult">The original <see cref="Result"/> value.</param>
/// <returns>The called <see cref="Result"/> value.</returns>
@ -98,37 +90,17 @@ namespace LibHac
return this;
}
[Conditional("DEBUG")]
private void LogImpl()
{
LogCallback?.Invoke(this);
}
[Conditional("DEBUG")]
private void LogConvertedImpl(Result originalResult)
{
ConvertedLogCallback?.Invoke(this, originalResult);
}
public delegate void ResultLogger(Result result);
public delegate void ConvertedResultLogger(Result result, Result originalResult);
public delegate bool ResultNameGetter(Result result, out string name);
public static ResultLogger LogCallback { get; set; }
public static ConvertedResultLogger ConvertedLogCallback { get; set; }
public static ResultNameGetter GetResultNameHandler { get; set; }
public bool TryGetResultName(out string name)
{
ResultNameGetter func = GetResultNameHandler;
IResultNameResolver resolver = NameResolver;
if (func == null)
if (resolver == null)
{
name = default;
return false;
}
return func(this, out name);
return resolver.TryResolveName(this, out name);
}
public string ToStringWithName()
@ -150,6 +122,40 @@ namespace LibHac
public static bool operator ==(Result left, Result right) => left.Equals(right);
public static bool operator !=(Result left, Result right) => !left.Equals(right);
public static void SetLogger(IResultLogger logger)
{
Logger = logger;
}
public static void SetNameResolver(IResultNameResolver nameResolver)
{
NameResolver = nameResolver;
}
[Conditional("DEBUG")]
private void LogImpl()
{
Logger?.LogResult(this);
}
[Conditional("DEBUG")]
private void LogConvertedImpl(Result originalResult)
{
Logger?.LogConvertedResult(this, originalResult);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static BaseType GetBitsValue(BaseType value, int bitsOffset, int bitsCount)
{
return (value >> bitsOffset) & ~(~default(BaseType) << bitsCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static BaseType SetBitsValue(int value, int bitsOffset, int bitsCount)
{
return ((uint)value & ~(~default(BaseType) << bitsCount)) << bitsOffset;
}
public struct Base
{
private const int DescriptionEndBitsOffset = ReservedBitsOffset;
@ -175,6 +181,7 @@ namespace LibHac
/// <summary>
/// The <see cref="Result"/> representing the start of this result range.
/// If returning a <see cref="Result"/> from a function, use <see cref="Log"/> instead.
/// </summary>
public Result Value => new Result((BaseType)_value);
@ -198,9 +205,11 @@ namespace LibHac
}
/// <summary>
/// A function that can contain code for logging or debugging returned results.
/// Intended to be used when returning a non-zero Result:
/// <code>return result.Log();</code>
/// Performs no action in release mode.
/// In debug mode, logs returned results using the <see cref="IResultLogger"/> set by <see cref="SetLogger"/>.
/// <br/>Intended to always be used when returning a non-zero <see cref="Result"/>.
/// <br/><br/>Example:
/// <code>return ResultFs.PathNotFound.Log();</code>
/// </summary>
/// <returns>The <see cref="Result"/> representing the start of this result range.</returns>
public Result Log()
@ -209,7 +218,7 @@ namespace LibHac
}
/// <summary>
/// Same as <see cref="Log"/>, but for when one result is converted to another.
/// In debug mode, logs converted results using the <see cref="IResultLogger"/> set by <see cref="SetLogger"/>.
/// </summary>
/// <param name="originalResult">The original <see cref="Result"/> value.</param>
/// <returns>The <see cref="Result"/> representing the start of this result range.</returns>
@ -230,5 +239,16 @@ namespace LibHac
return ((uint)value & ~(~default(ulong) << bitsCount)) << bitsOffset;
}
}
public interface IResultLogger
{
public void LogResult(Result result);
public void LogConvertedResult(Result result, Result originalResult);
}
public interface IResultNameResolver
{
public bool TryResolveName(Result result, out string name);
}
}
}

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Text;
using LibHac;
@ -52,11 +52,11 @@ namespace hactoolnet
if (ctx.Options == null) return false;
StreamWriter logWriter = null;
StreamWriter resultWriter = null;
ResultLogger resultLogger = null;
try
{
Result.GetResultNameHandler = ResultLogFunctions.TryGetResultName;
Result.SetNameResolver(new ResultNameResolver());
using (var logger = new ProgressBar())
{
@ -76,11 +76,10 @@ namespace hactoolnet
if (ctx.Options.ResultLog != null)
{
resultWriter = new StreamWriter(ctx.Options.ResultLog);
ResultLogFunctions.LogWriter = resultWriter;
resultLogger = new ResultLogger(new StreamWriter(ctx.Options.ResultLog),
printStackTrace: true, printSourceInfo: true, combineRepeats: true);
Result.LogCallback = ResultLogFunctions.LogResult;
Result.ConvertedLogCallback = ResultLogFunctions.LogConvertedResult;
Result.SetLogger(resultLogger);
}
OpenKeyset(ctx);
@ -97,9 +96,12 @@ namespace hactoolnet
finally
{
logWriter?.Dispose();
resultWriter?.Dispose();
ResultLogFunctions.LogWriter = null;
if (resultLogger != null)
{
Result.SetLogger(null);
resultLogger.Dispose();
}
}
return true;

View File

@ -1,87 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using LibHac;
using LibHac.Fs;
namespace hactoolnet
{
public static class ResultLogFunctions
{
private static Dictionary<Result, string> ResultNames { get; } = GetResultNames();
public static TextWriter LogWriter { get; set; }
public static void LogResult(Result result)
{
if (LogWriter == null) return;
var st = new StackTrace(2, true);
if (st.FrameCount > 1)
{
MethodBase method = st.GetFrame(0).GetMethod();
// This result from these functions is usually noise because they
// are frequently used to detect if a file exists
if (ResultFs.PathNotFound.Includes(result) &&
typeof(IFileSystem).IsAssignableFrom(method.DeclaringType) &&
method.Name.StartsWith(nameof(IFileSystem.GetEntryType)) ||
method.Name.StartsWith(nameof(IAttributeFileSystem.GetFileAttributes)))
{
return;
}
string methodName = $"{method.DeclaringType?.FullName}.{method.Name}";
LogWriter.WriteLine($"{result.ToStringWithName()} returned by {methodName}");
LogWriter.WriteLine(st);
}
}
public static void LogConvertedResult(Result result, Result originalResult)
{
if (LogWriter == null) return;
var st = new StackTrace(2, false);
if (st.FrameCount > 1)
{
MethodBase method = st.GetFrame(0).GetMethod();
string methodName = $"{method.DeclaringType?.FullName}.{method.Name}";
LogWriter.WriteLine($"{originalResult.ToStringWithName()} was converted to {result.ToStringWithName()} by {methodName}");
}
}
public static Dictionary<Result, string> GetResultNames()
{
var dict = new Dictionary<Result, string>();
Assembly assembly = typeof(Result).Assembly;
foreach (Type type in assembly.GetTypes().Where(x => x.Name.Contains("Result")))
{
foreach (PropertyInfo property in type.GetProperties()
.Where(x => x.PropertyType == typeof(Result) && x.GetMethod.IsStatic && x.SetMethod == null))
{
var value = (Result)property.GetValue(null, null);
string name = $"{type.Name}{property.Name}";
dict.Add(value, name);
}
}
return dict;
}
public static bool TryGetResultName(Result result, out string name)
{
return ResultNames.TryGetValue(result, out name);
}
}
}

View File

@ -0,0 +1,218 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using LibHac;
using LibHac.Fs;
namespace hactoolnet
{
internal class ResultLogger : Result.IResultLogger, IDisposable
{
private TextWriter Writer { get; }
private bool PrintStackTrace { get; }
private bool PrintSourceInfo { get; }
private bool CombineRepeats { get; }
private LogEntry _pendingEntry;
private bool LastEntryPrintedNewLine { get; set; } = true;
public ResultLogger(TextWriter writer, bool printStackTrace, bool printSourceInfo, bool combineRepeats)
{
Writer = writer ?? throw new ArgumentNullException(nameof(writer));
PrintStackTrace = printStackTrace;
PrintSourceInfo = printSourceInfo;
CombineRepeats = combineRepeats;
bool isDebugMode = false;
CheckIfDebugMode(ref isDebugMode);
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (!isDebugMode)
{
Writer.WriteLine("The result log is only enabled when running in debug mode.");
}
}
public void LogResult(Result result)
{
StackTrace st = GetStackTrace();
MethodBase method = st.GetFrame(0).GetMethod();
// This result from these functions is usually noise because they
// are frequently used to detect if a file exists
if (ResultFs.PathNotFound.Includes(result) &&
typeof(IFileSystem).IsAssignableFrom(method.DeclaringType) &&
method.Name.StartsWith(nameof(IFileSystem.GetEntryType)) ||
method.Name.StartsWith(nameof(IAttributeFileSystem.GetFileAttributes)))
{
return;
}
AddLogEntry(new LogEntry(result, st));
}
public void LogConvertedResult(Result result, Result originalResult)
{
StackTrace st = GetStackTrace();
AddLogEntry(new LogEntry(result, st, originalResult));
}
private void AddLogEntry(LogEntry entry)
{
if (CombineRepeats && _pendingEntry.IsRepeat(entry, PrintStackTrace && !entry.IsConvertedResult))
{
_pendingEntry.TimesCalled++;
return;
}
PrintPendingEntry();
if (CombineRepeats)
{
_pendingEntry = entry;
}
else
{
PrintLogEntry(entry);
}
}
private void PrintPendingEntry()
{
if (_pendingEntry.StackTrace != null)
{
PrintLogEntry(_pendingEntry);
_pendingEntry = default;
}
}
private void PrintLogEntry(LogEntry entry)
{
MethodBase method = entry.StackTrace.GetFrame(0).GetMethod();
string methodName = $"{method.DeclaringType?.FullName}.{method.Name}";
bool printStackTrace = PrintStackTrace && !entry.IsConvertedResult;
// Make sure there's a new line if printing a stack trace
// A stack trace includes a new line at the end of it, so add the new line only if needed
string entryText = printStackTrace && !LastEntryPrintedNewLine ? Environment.NewLine : string.Empty;
string lineNumber = entry.LineNumber > 0 ? $":line{entry.LineNumber}" : string.Empty;
if (entry.IsConvertedResult)
{
entryText += $"{entry.OriginalResult.ToStringWithName()} was converted to {entry.Result.ToStringWithName()} by {methodName}{lineNumber}";
}
else
{
entryText += $"{entry.Result.ToStringWithName()} was returned by {methodName}{lineNumber}";
}
if (entry.TimesCalled > 1)
{
entryText += $" {entry.TimesCalled} times";
}
Writer.WriteLine(entryText);
if (printStackTrace)
{
Writer.WriteLine(entry.StackTraceText);
}
LastEntryPrintedNewLine = printStackTrace;
}
// Returns the stack trace starting at the method that called Log()
private StackTrace GetStackTrace()
{
var st = new StackTrace();
int framesToSkip = 0;
for (; framesToSkip < st.FrameCount; framesToSkip++)
{
Type declaringType = st.GetFrame(framesToSkip)?.GetMethod()?.DeclaringType;
if (declaringType == null)
{
framesToSkip = 0;
break;
}
if (declaringType != typeof(ResultLogger) &&
declaringType != typeof(Result) &&
declaringType != typeof(Result.Base))
{
break;
}
}
return new StackTrace(framesToSkip, PrintSourceInfo);
}
// You can't negate a conditional attribute, so this is a hacky workaround
[Conditional("DEBUG")]
// ReSharper disable once RedundantAssignment
private void CheckIfDebugMode(ref bool isDebugMode)
{
isDebugMode = true;
}
public void Dispose()
{
PrintPendingEntry();
Writer.Dispose();
}
private struct LogEntry
{
public Result Result { get; }
public Result OriginalResult { get; }
public string CallingMethod { get; }
public StackTrace StackTrace { get; }
public string StackTraceText { get; }
// Line number will be 0 if there's no source info
public int LineNumber { get; }
public int TimesCalled { get; set; }
public bool IsConvertedResult { get; }
public LogEntry(Result result, StackTrace stackTrace) : this(result, stackTrace, false, default) { }
public LogEntry(Result result, StackTrace stackTrace, Result originalResult) : this(result, stackTrace, true, originalResult) { }
private LogEntry(Result result, StackTrace stackTrace, bool isConverted, Result originalResult)
{
Result = result;
StackTrace = stackTrace;
IsConvertedResult = isConverted;
OriginalResult = originalResult;
MethodBase method = stackTrace.GetFrame(0).GetMethod();
CallingMethod = $"{method.DeclaringType?.FullName}.{method.Name}";
StackTraceText = stackTrace.ToString();
LineNumber = stackTrace.GetFrame(0).GetFileLineNumber();
TimesCalled = 1;
}
public bool IsRepeat(LogEntry other, bool compareByStackTrace)
{
if (Result != other.Result ||
IsConvertedResult != other.IsConvertedResult ||
(IsConvertedResult && OriginalResult != other.OriginalResult))
{
return false;
}
if (compareByStackTrace)
{
return StackTraceText == other.StackTraceText;
}
return LineNumber == other.LineNumber && CallingMethod == other.CallingMethod;
}
}
}
}

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using LibHac;
namespace hactoolnet
{
internal class ResultNameResolver : Result.IResultNameResolver
{
private Lazy<Dictionary<Result, string>> ResultNames { get; } = new Lazy<Dictionary<Result, string>>(GetResultNames);
public bool TryResolveName(Result result, out string name)
{
return ResultNames.Value.TryGetValue(result, out name);
}
private static Dictionary<Result, string> GetResultNames()
{
var dict = new Dictionary<Result, string>();
Assembly assembly = typeof(Result).Assembly;
foreach (TypeInfo type in assembly.DefinedTypes.Where(x => x.Name.Contains("Result")))
foreach (PropertyInfo property in type.DeclaredProperties
.Where(x => x.PropertyType == typeof(Result.Base) && x.GetMethod.IsStatic && x.SetMethod == null))
{
Result value = ((Result.Base)property.GetValue(null, null)).Value;
string name = $"{type.Name}{property.Name}";
dict[value] = name;
}
return dict;
}
}
}