mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add Result methods for debugging (#90)
- Allow setting a callback function for when Result.Log is called. - Allow setting a function that returns a name for a Result value. - Print Result name in error messages
This commit is contained in:
parent
172817a7d5
commit
cccb811293
6 changed files with 145 additions and 4 deletions
|
@ -82,10 +82,10 @@ namespace LibHac
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(InnerMessage))
|
if (!string.IsNullOrWhiteSpace(InnerMessage))
|
||||||
{
|
{
|
||||||
return $"{ResultValue.ErrorCode}: {InnerMessage}";
|
return $"{ResultValue.ToStringWithName()}: {InnerMessage}";
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultValue.ErrorCode;
|
return ResultValue.ToStringWithName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ using System.Diagnostics;
|
||||||
namespace LibHac
|
namespace LibHac
|
||||||
{
|
{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
[DebuggerDisplay("{ToString()}")]
|
[DebuggerDisplay("{ToStringWithName(),nq}")]
|
||||||
public struct Result : IEquatable<Result>
|
public struct Result : IEquatable<Result>
|
||||||
{
|
{
|
||||||
public readonly int Value;
|
public readonly int Value;
|
||||||
|
@ -42,6 +42,9 @@ namespace LibHac
|
||||||
/// <returns>The called <see cref="Result"/> value.</returns>
|
/// <returns>The called <see cref="Result"/> value.</returns>
|
||||||
public Result Log()
|
public Result Log()
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
|
LogCallback?.Invoke(this);
|
||||||
|
#endif
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,9 +55,43 @@ namespace LibHac
|
||||||
/// <returns>The called <see cref="Result"/> value.</returns>
|
/// <returns>The called <see cref="Result"/> value.</returns>
|
||||||
public Result LogConverted(Result originalResult)
|
public Result LogConverted(Result originalResult)
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
|
ConvertedLogCallback?.Invoke(this, originalResult);
|
||||||
|
#endif
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (func == null)
|
||||||
|
{
|
||||||
|
name = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(this, out name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ToStringWithName()
|
||||||
|
{
|
||||||
|
if (TryGetResultName(out string name))
|
||||||
|
{
|
||||||
|
return $"{name} ({ErrorCode})";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCode;
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return IsSuccess() ? "Success" : ErrorCode;
|
return IsSuccess() ? "Success" : ErrorCode;
|
||||||
|
|
|
@ -19,6 +19,7 @@ namespace hactoolnet
|
||||||
new CliOption("titlekeys", 1, (o, a) => o.TitleKeyFile = a[0]),
|
new CliOption("titlekeys", 1, (o, a) => o.TitleKeyFile = a[0]),
|
||||||
new CliOption("consolekeys", 1, (o, a) => o.ConsoleKeyFile = a[0]),
|
new CliOption("consolekeys", 1, (o, a) => o.ConsoleKeyFile = a[0]),
|
||||||
new CliOption("accesslog", 1, (o, a) => o.AccessLog = a[0]),
|
new CliOption("accesslog", 1, (o, a) => o.AccessLog = a[0]),
|
||||||
|
new CliOption("resultlog", 1, (o, a) => o.ResultLog = a[0]),
|
||||||
new CliOption("section0", 1, (o, a) => o.SectionOut[0] = a[0]),
|
new CliOption("section0", 1, (o, a) => o.SectionOut[0] = a[0]),
|
||||||
new CliOption("section1", 1, (o, a) => o.SectionOut[1] = a[0]),
|
new CliOption("section1", 1, (o, a) => o.SectionOut[1] = a[0]),
|
||||||
new CliOption("section2", 1, (o, a) => o.SectionOut[2] = a[0]),
|
new CliOption("section2", 1, (o, a) => o.SectionOut[2] = a[0]),
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace hactoolnet
|
||||||
public string TitleKeyFile;
|
public string TitleKeyFile;
|
||||||
public string ConsoleKeyFile;
|
public string ConsoleKeyFile;
|
||||||
public string AccessLog;
|
public string AccessLog;
|
||||||
|
public string ResultLog;
|
||||||
public string[] SectionOut = new string[4];
|
public string[] SectionOut = new string[4];
|
||||||
public string[] SectionOutDir = new string[4];
|
public string[] SectionOutDir = new string[4];
|
||||||
public string HeaderOut;
|
public string HeaderOut;
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace hactoolnet
|
||||||
|
|
||||||
if (ex.ResultValue != ex.InternalResultValue)
|
if (ex.ResultValue != ex.InternalResultValue)
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine($"Internal Code: {ex.InternalResultValue.ErrorCode}");
|
Console.Error.WriteLine($"Internal Code: {ex.InternalResultValue.ToStringWithName()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.Error.WriteLine();
|
Console.Error.WriteLine();
|
||||||
|
@ -52,9 +52,12 @@ namespace hactoolnet
|
||||||
if (ctx.Options == null) return false;
|
if (ctx.Options == null) return false;
|
||||||
|
|
||||||
StreamWriter logWriter = null;
|
StreamWriter logWriter = null;
|
||||||
|
StreamWriter resultWriter = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Result.GetResultNameHandler = ResultLogFunctions.TryGetResultName;
|
||||||
|
|
||||||
using (var logger = new ProgressBar())
|
using (var logger = new ProgressBar())
|
||||||
{
|
{
|
||||||
ctx.Logger = logger;
|
ctx.Logger = logger;
|
||||||
|
@ -71,6 +74,15 @@ namespace hactoolnet
|
||||||
ctx.Horizon.Fs.SetAccessLogObject(accessLog);
|
ctx.Horizon.Fs.SetAccessLogObject(accessLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx.Options.ResultLog != null)
|
||||||
|
{
|
||||||
|
resultWriter = new StreamWriter(ctx.Options.ResultLog);
|
||||||
|
ResultLogFunctions.LogWriter = resultWriter;
|
||||||
|
|
||||||
|
Result.LogCallback = ResultLogFunctions.LogResult;
|
||||||
|
Result.ConvertedLogCallback = ResultLogFunctions.LogConvertedResult;
|
||||||
|
}
|
||||||
|
|
||||||
OpenKeyset(ctx);
|
OpenKeyset(ctx);
|
||||||
|
|
||||||
if (ctx.Options.RunCustom)
|
if (ctx.Options.RunCustom)
|
||||||
|
@ -85,6 +97,9 @@ namespace hactoolnet
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
logWriter?.Dispose();
|
logWriter?.Dispose();
|
||||||
|
resultWriter?.Dispose();
|
||||||
|
|
||||||
|
ResultLogFunctions.LogWriter = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
87
src/hactoolnet/ResultLog.cs
Normal file
87
src/hactoolnet/ResultLog.cs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
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 (result == ResultFs.PathNotFound &&
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue