From 9934f477d5f06d9e4efbf739a47cd1676e838297 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Thu, 3 Oct 2019 23:23:30 -0500 Subject: [PATCH] Add remaining GetRightsId methods and U8StringBuilder --- src/LibHac/Common/PaddingStructs.cs | 38 ++++++++++++ src/LibHac/Common/U8StringBuilder.cs | 86 ++++++++++++++++++++++++++++ src/LibHac/Fs/ExternalKeys.cs | 29 +++++++++- src/LibHac/FsSystem/FsPath.cs | 21 ++++++- src/LibHac/Ncm/TitleId.cs | 12 +++- 5 files changed, 180 insertions(+), 6 deletions(-) create mode 100644 src/LibHac/Common/PaddingStructs.cs create mode 100644 src/LibHac/Common/U8StringBuilder.cs diff --git a/src/LibHac/Common/PaddingStructs.cs b/src/LibHac/Common/PaddingStructs.cs new file mode 100644 index 00000000..92ac14d7 --- /dev/null +++ b/src/LibHac/Common/PaddingStructs.cs @@ -0,0 +1,38 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace LibHac.Common +{ + // In order for the Visual Studio debugger to accurately display a struct, every offset + // in the struct that is used for the debugger display must be part of a field. + // These padding structs make it easier to accomplish that. + [StructLayout(LayoutKind.Sequential, Size = 0x20)] + internal struct Padding20 + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding00; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding08; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding10; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding18; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x40)] + internal struct Padding40 + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding20 Padding00; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding20 Padding20; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x80)] + internal struct Padding80 + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding40 Padding00; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding40 Padding40; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x100)] + internal struct Padding100 + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding00; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding80; + } +} diff --git a/src/LibHac/Common/U8StringBuilder.cs b/src/LibHac/Common/U8StringBuilder.cs new file mode 100644 index 00000000..cbaed2f1 --- /dev/null +++ b/src/LibHac/Common/U8StringBuilder.cs @@ -0,0 +1,86 @@ +using System; +using System.Diagnostics; + +namespace LibHac.Common +{ + [DebuggerDisplay("{ToString()}")] + public ref struct U8StringBuilder + { + private const int NullTerminatorLength = 1; + + private readonly Span _buffer; + private int _length; + + public bool Overflowed { get; private set; } + public int Capacity => _buffer.Length - NullTerminatorLength; + + public U8StringBuilder(Span buffer) + { + _buffer = buffer; + _length = 0; + Overflowed = false; + + ThrowIfBufferLengthIsZero(); + + AddNullTerminator(); + } + + public U8StringBuilder Append(ReadOnlySpan value) + { + if (Overflowed) return this; + + int valueLength = StringUtils.GetLength(value); + + if (!HasAdditionalCapacity(valueLength)) + { + Overflowed = true; + return this; + } + + value.Slice(0, valueLength).CopyTo(_buffer.Slice(_length)); + _length += valueLength; + AddNullTerminator(); + + return this; + } + + public U8StringBuilder Append(byte value) + { + if (Overflowed) return this; + + if (!HasAdditionalCapacity(1)) + { + Overflowed = true; + return this; + } + + _buffer[_length] = value; + _length++; + AddNullTerminator(); + + return this; + } + + private bool HasCapacity(int requiredCapacity) + { + return requiredCapacity <= Capacity; + } + + private bool HasAdditionalCapacity(int requiredAdditionalCapacity) + { + return HasCapacity(_length + requiredAdditionalCapacity); + } + + private void AddNullTerminator() + { + _buffer[_length] = 0; + } + + private void ThrowIfBufferLengthIsZero() + { + if (_buffer.Length == 0) throw new ArgumentException("Buffer length must be greater than 0."); + } + + public override string ToString() => StringUtils.Utf8ZToString(_buffer); + } +} diff --git a/src/LibHac/Fs/ExternalKeys.cs b/src/LibHac/Fs/ExternalKeys.cs index 8ee8b883..e0638600 100644 --- a/src/LibHac/Fs/ExternalKeys.cs +++ b/src/LibHac/Fs/ExternalKeys.cs @@ -1,4 +1,6 @@ -using LibHac.FsService; +using LibHac.Common; +using LibHac.FsService; +using LibHac.FsSystem; using LibHac.Ncm; using LibHac.Spl; @@ -14,6 +16,31 @@ namespace LibHac.Fs return fsProxy.GetRightsId(out rightsId, programId, storageId); } + public static Result GetRightsId(this FileSystemClient fs, out RightsId rightsId, U8Span path) + { + rightsId = default; + + IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); + + Result rc = FsPath.FromSpan(out FsPath fsPath, path); + if (rc.IsFailure()) return rc; + + return fsProxy.GetRightsIdByPath(out rightsId, ref fsPath); + } + + public static Result GetRightsId(this FileSystemClient fs, out RightsId rightsId, out byte keyGeneration, U8Span path) + { + rightsId = default; + keyGeneration = default; + + IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); + + Result rc = FsPath.FromSpan(out FsPath fsPath, path); + if (rc.IsFailure()) return rc; + + return fsProxy.GetRightsIdAndKeyGenerationByPath(out rightsId, out keyGeneration, ref fsPath); + } + public static Result RegisterExternalKey(this FileSystemClient fs, ref RightsId rightsId, ref AccessKey key) { IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject(); diff --git a/src/LibHac/FsSystem/FsPath.cs b/src/LibHac/FsSystem/FsPath.cs index af82bb49..6da1475e 100644 --- a/src/LibHac/FsSystem/FsPath.cs +++ b/src/LibHac/FsSystem/FsPath.cs @@ -2,18 +2,33 @@ using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Fs; namespace LibHac.FsSystem { [DebuggerDisplay("{ToString()}")] - [StructLayout(LayoutKind.Explicit, Size = MaxLength + 1)] + [StructLayout(LayoutKind.Sequential, Size = MaxLength + 1)] public struct FsPath { internal const int MaxLength = 0x300; - [FieldOffset(0)] private byte _str; +#if DEBUG + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding200; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly byte Padding300; +#endif - public Span Str => SpanHelpers.CreateSpan(ref _str, MaxLength + 1); + public Span Str => SpanHelpers.AsByteSpan(ref this); + + public static Result FromSpan(out FsPath fsPath, ReadOnlySpan path) + { + fsPath = new FsPath(); + + U8StringBuilder builder = new U8StringBuilder(fsPath.Str).Append(path); + + return builder.Overflowed ? ResultFs.TooLongPath : Result.Success; + } public static implicit operator U8Span(FsPath value) => new U8Span(value.Str); public override string ToString() => StringUtils.Utf8ZToString(Str); diff --git a/src/LibHac/Ncm/TitleId.cs b/src/LibHac/Ncm/TitleId.cs index 9502bf10..0288bd7b 100644 --- a/src/LibHac/Ncm/TitleId.cs +++ b/src/LibHac/Ncm/TitleId.cs @@ -1,8 +1,16 @@ -namespace LibHac.Ncm +using System.Diagnostics; + +namespace LibHac.Ncm { + [DebuggerDisplay("{" + nameof(Value) + "}")] public struct TitleId { - public ulong Value; + public readonly ulong Value; + + public TitleId(ulong value) + { + Value = value; + } public static explicit operator ulong(TitleId titleId) => titleId.Value; }