mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Misc additions and warning fixes (#134)
* Run codegen and fix DeliveryCacheFileMetaEntry layout * Use KipReader instead of Kip * Ensure feature parity between U8String structs * Don't use the obsolete Nacp class * Add some null checks * Address ReSharper warnings * The Result structs should be readonly
This commit is contained in:
parent
44e4c7a311
commit
cb8b088487
25 changed files with 228 additions and 62 deletions
|
@ -1,6 +1,4 @@
|
||||||
using LibHac.Ncm;
|
namespace LibHac.Bcat.Detail.Ipc
|
||||||
|
|
||||||
namespace LibHac.Bcat.Detail.Ipc
|
|
||||||
{
|
{
|
||||||
public interface IServiceCreator
|
public interface IServiceCreator
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,7 +6,8 @@ namespace LibHac.Bcat.Detail.Service.Core
|
||||||
internal struct DeliveryCacheFileMetaEntry
|
internal struct DeliveryCacheFileMetaEntry
|
||||||
{
|
{
|
||||||
[FieldOffset(0x00)] public FileName Name;
|
[FieldOffset(0x00)] public FileName Name;
|
||||||
[FieldOffset(0x20)] public long Size;
|
[FieldOffset(0x20)] public long Id;
|
||||||
|
[FieldOffset(0x28)] public long Size;
|
||||||
[FieldOffset(0x30)] public Digest Digest;
|
[FieldOffset(0x30)] public Digest Digest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,16 @@ namespace LibHac.Bcat
|
||||||
public static Result.Base NotFound => new Result.Base(ModuleBcat, 2);
|
public static Result.Base NotFound => new Result.Base(ModuleBcat, 2);
|
||||||
/// <summary>Error code: 2122-0003; Inner value: 0x67a</summary>
|
/// <summary>Error code: 2122-0003; Inner value: 0x67a</summary>
|
||||||
public static Result.Base TargetLocked => new Result.Base(ModuleBcat, 3);
|
public static Result.Base TargetLocked => new Result.Base(ModuleBcat, 3);
|
||||||
|
/// <summary>Error code: 2122-0004; Inner value: 0x87a</summary>
|
||||||
|
public static Result.Base TargetAlreadyMounted => new Result.Base(ModuleBcat, 4);
|
||||||
|
/// <summary>Error code: 2122-0005; Inner value: 0xa7a</summary>
|
||||||
|
public static Result.Base TargetNotMounted => new Result.Base(ModuleBcat, 5);
|
||||||
/// <summary>Error code: 2122-0006; Inner value: 0xc7a</summary>
|
/// <summary>Error code: 2122-0006; Inner value: 0xc7a</summary>
|
||||||
public static Result.Base AlreadyOpen => new Result.Base(ModuleBcat, 6);
|
public static Result.Base AlreadyOpen => new Result.Base(ModuleBcat, 6);
|
||||||
/// <summary>Error code: 2122-0007; Inner value: 0xe7a</summary>
|
/// <summary>Error code: 2122-0007; Inner value: 0xe7a</summary>
|
||||||
public static Result.Base NotOpen => new Result.Base(ModuleBcat, 7);
|
public static Result.Base NotOpen => new Result.Base(ModuleBcat, 7);
|
||||||
|
/// <summary>Error code: 2122-0008; Inner value: 0x107a</summary>
|
||||||
|
public static Result.Base InternetRequestDenied => new Result.Base(ModuleBcat, 8);
|
||||||
/// <summary>Error code: 2122-0009; Inner value: 0x127a</summary>
|
/// <summary>Error code: 2122-0009; Inner value: 0x127a</summary>
|
||||||
public static Result.Base ServiceOpenLimitReached => new Result.Base(ModuleBcat, 9);
|
public static Result.Base ServiceOpenLimitReached => new Result.Base(ModuleBcat, 9);
|
||||||
/// <summary>Error code: 2122-0010; Inner value: 0x147a</summary>
|
/// <summary>Error code: 2122-0010; Inner value: 0x147a</summary>
|
||||||
|
@ -33,10 +39,14 @@ namespace LibHac.Bcat
|
||||||
public static Result.Base NetworkServiceAccountNotAvailable => new Result.Base(ModuleBcat, 31);
|
public static Result.Base NetworkServiceAccountNotAvailable => new Result.Base(ModuleBcat, 31);
|
||||||
/// <summary>Error code: 2122-0080; Inner value: 0xa07a</summary>
|
/// <summary>Error code: 2122-0080; Inner value: 0xa07a</summary>
|
||||||
public static Result.Base PassphrasePathNotFound => new Result.Base(ModuleBcat, 80);
|
public static Result.Base PassphrasePathNotFound => new Result.Base(ModuleBcat, 80);
|
||||||
|
/// <summary>Error code: 2122-0081; Inner value: 0xa27a</summary>
|
||||||
|
public static Result.Base DataVerificationFailed => new Result.Base(ModuleBcat, 81);
|
||||||
/// <summary>Error code: 2122-0090; Inner value: 0xb47a</summary>
|
/// <summary>Error code: 2122-0090; Inner value: 0xb47a</summary>
|
||||||
public static Result.Base PermissionDenied => new Result.Base(ModuleBcat, 90);
|
public static Result.Base PermissionDenied => new Result.Base(ModuleBcat, 90);
|
||||||
/// <summary>Error code: 2122-0091; Inner value: 0xb67a</summary>
|
/// <summary>Error code: 2122-0091; Inner value: 0xb67a</summary>
|
||||||
public static Result.Base AllocationFailed => new Result.Base(ModuleBcat, 91);
|
public static Result.Base AllocationFailed => new Result.Base(ModuleBcat, 91);
|
||||||
|
/// <summary>Error code: 2122-0098; Inner value: 0xc47a</summary>
|
||||||
|
public static Result.Base InvalidOperation => new Result.Base(ModuleBcat, 98);
|
||||||
/// <summary>Error code: 2122-0204; Inner value: 0x1987a</summary>
|
/// <summary>Error code: 2122-0204; Inner value: 0x1987a</summary>
|
||||||
public static Result.Base InvalidDeliveryCacheStorageFile => new Result.Base(ModuleBcat, 204);
|
public static Result.Base InvalidDeliveryCacheStorageFile => new Result.Base(ModuleBcat, 204);
|
||||||
/// <summary>Error code: 2122-0205; Inner value: 0x19a7a</summary>
|
/// <summary>Error code: 2122-0205; Inner value: 0x19a7a</summary>
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
[DebuggerDisplay("{ToString()}")]
|
||||||
public ref struct U8SpanMutable
|
public readonly ref struct U8SpanMutable
|
||||||
{
|
{
|
||||||
private readonly Span<byte> _buffer;
|
private readonly Span<byte> _buffer;
|
||||||
|
|
||||||
|
@ -28,6 +30,29 @@ namespace LibHac.Common
|
||||||
_buffer = Encoding.UTF8.GetBytes(value);
|
_buffer = Encoding.UTF8.GetBytes(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public byte GetOrNull(int i)
|
||||||
|
{
|
||||||
|
byte value = 0;
|
||||||
|
|
||||||
|
if ((uint)i < (uint)_buffer.Length)
|
||||||
|
{
|
||||||
|
value = GetUnsafe(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public byte GetUnsafe(int i)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
return _buffer[i];
|
||||||
|
#else
|
||||||
|
return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
public U8SpanMutable Slice(int start)
|
public U8SpanMutable Slice(int start)
|
||||||
{
|
{
|
||||||
return new U8SpanMutable(_buffer.Slice(start));
|
return new U8SpanMutable(_buffer.Slice(start));
|
||||||
|
@ -56,6 +81,18 @@ namespace LibHac.Common
|
||||||
return new U8StringMutable(_buffer.ToArray());
|
return new U8StringMutable(_buffer.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsNull() => _buffer == default;
|
/// <summary>
|
||||||
|
/// Checks if the <see cref="U8StringMutable"/> has no buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see langword="true"/> if the span has no buffer.
|
||||||
|
/// Otherwise, <see langword="false"/>.</returns>
|
||||||
|
public bool IsNull() => _buffer.IsEmpty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the <see cref="U8StringMutable"/> has no buffer or begins with a null terminator.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see langword="true"/> if the span has no buffer or begins with a null terminator.
|
||||||
|
/// Otherwise, <see langword="false"/>.</returns>
|
||||||
|
public bool IsEmpty() => _buffer.IsEmpty || MemoryMarshal.GetReference(_buffer) == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ using System.Text;
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
[DebuggerDisplay("{ToString()}")]
|
||||||
public struct U8String
|
public readonly struct U8String
|
||||||
{
|
{
|
||||||
private readonly byte[] _buffer;
|
private readonly byte[] _buffer;
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ using System.Text;
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
[DebuggerDisplay("{ToString()}")]
|
||||||
public struct U8StringMutable
|
public readonly struct U8StringMutable
|
||||||
{
|
{
|
||||||
private readonly byte[] _buffer;
|
private readonly byte[] _buffer;
|
||||||
|
|
||||||
|
@ -53,6 +53,18 @@ namespace LibHac.Common
|
||||||
return StringUtils.Utf8ZToString(_buffer);
|
return StringUtils.Utf8ZToString(_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the <see cref="U8String"/> has no buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see langword="true"/> if the string has no buffer.
|
||||||
|
/// Otherwise, <see langword="false"/>.</returns>
|
||||||
public bool IsNull() => _buffer == null;
|
public bool IsNull() => _buffer == null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the <see cref="U8String"/> has no buffer or begins with a null terminator.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see langword="true"/> if the string has no buffer or begins with a null terminator.
|
||||||
|
/// Otherwise, <see langword="false"/>.</returns>
|
||||||
|
public bool IsEmpty() => _buffer == null || _buffer.Length < 1 || _buffer[0] == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -265,7 +265,13 @@ namespace LibHac.Fs
|
||||||
|
|
||||||
entry.Type = DirectoryEntryType.File;
|
entry.Type = DirectoryEntryType.File;
|
||||||
entry.Attributes = CurrentFile.Attributes;
|
entry.Attributes = CurrentFile.Attributes;
|
||||||
CurrentFile.File.GetSize(out entry.Size);
|
|
||||||
|
Result rc = CurrentFile.File.GetSize(out entry.Size);
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
entriesRead = 0;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
CurrentFile = CurrentFile.Next;
|
CurrentFile = CurrentFile.Next;
|
||||||
|
|
|
@ -145,10 +145,19 @@ namespace LibHac.FsSystem
|
||||||
Blocks.AddFirst(node);
|
Blocks.AddFirst(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a supposedly active block is null, something's really wrong
|
||||||
|
if (node is null)
|
||||||
|
{
|
||||||
|
throw new NullReferenceException("CachedStorage cache block is unexpectedly null.");
|
||||||
|
}
|
||||||
|
|
||||||
return node.Value;
|
return node.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = Blocks.Last;
|
// An inactive node shouldn't be null, but we'll fix it if it is anyway
|
||||||
|
node = Blocks.Last ??
|
||||||
|
new LinkedListNode<CacheBlock>(new CacheBlock {Buffer = new byte[BlockSize], Index = -1});
|
||||||
|
|
||||||
FlushBlock(node.Value);
|
FlushBlock(node.Value);
|
||||||
|
|
||||||
CacheBlock block = node.Value;
|
CacheBlock block = node.Value;
|
||||||
|
|
|
@ -8,19 +8,25 @@ namespace LibHac.FsSystem
|
||||||
{
|
{
|
||||||
public static class StorageExtensions
|
public static class StorageExtensions
|
||||||
{
|
{
|
||||||
|
[Obsolete("The standard Span-based IStorage methods should be used instead.")]
|
||||||
public static Result Read(this IStorage storage, long offset, byte[] buffer, int count, int bufferOffset)
|
public static Result Read(this IStorage storage, long offset, byte[] buffer, int count, int bufferOffset)
|
||||||
{
|
{
|
||||||
ValidateStorageParameters(buffer, offset, count, bufferOffset);
|
ValidateStorageParameters(buffer, offset, count, bufferOffset);
|
||||||
return storage.Read(offset, buffer.AsSpan(bufferOffset, count));
|
return storage.Read(offset, buffer.AsSpan(bufferOffset, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("The standard Span-based IStorage methods should be used instead.")]
|
||||||
public static Result Write(this IStorage storage, long offset, byte[] buffer, int count, int bufferOffset)
|
public static Result Write(this IStorage storage, long offset, byte[] buffer, int count, int bufferOffset)
|
||||||
{
|
{
|
||||||
ValidateStorageParameters(buffer, offset, count, bufferOffset);
|
ValidateStorageParameters(buffer, offset, count, bufferOffset);
|
||||||
return storage.Write(offset, buffer.AsSpan(bufferOffset, count));
|
return storage.Write(offset, buffer.AsSpan(bufferOffset, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: remove this method when the above Read and Write methods are removed
|
||||||
|
// (Hopefully they're still above, or else someone didn't listen)
|
||||||
|
// ReSharper disable ParameterOnlyUsedForPreconditionCheck.Local
|
||||||
private static void ValidateStorageParameters(byte[] buffer, long offset, int count, int bufferOffset)
|
private static void ValidateStorageParameters(byte[] buffer, long offset, int count, int bufferOffset)
|
||||||
|
// ReSharper restore ParameterOnlyUsedForPreconditionCheck.Local
|
||||||
{
|
{
|
||||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||||
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), "Argument must be non-negative.");
|
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), "Argument must be non-negative.");
|
||||||
|
|
|
@ -7,6 +7,7 @@ namespace LibHac
|
||||||
{
|
{
|
||||||
public class HorizonClient
|
public class HorizonClient
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||||
private Horizon Horizon { get; }
|
private Horizon Horizon { get; }
|
||||||
|
|
||||||
private Lazy<ArpClient> ArpLazy { get; }
|
private Lazy<ArpClient> ArpLazy { get; }
|
||||||
|
|
|
@ -63,8 +63,8 @@ namespace LibHac
|
||||||
protected HorizonResultException(SerializationInfo info, StreamingContext context)
|
protected HorizonResultException(SerializationInfo info, StreamingContext context)
|
||||||
: base(info, context)
|
: base(info, context)
|
||||||
{
|
{
|
||||||
InternalResultValue = (Result)info.GetValue(nameof(InternalResultValue), InternalResultValue.GetType());
|
InternalResultValue = (Result)(info.GetValue(nameof(InternalResultValue), InternalResultValue.GetType()) ?? default(Result));
|
||||||
ResultValue = (Result)info.GetValue(nameof(ResultValue), ResultValue.GetType());
|
ResultValue = (Result)(info.GetValue(nameof(ResultValue), ResultValue.GetType()) ?? default(Result));
|
||||||
InnerMessage = (string)info.GetValue(nameof(InnerMessage), InnerMessage.GetType());
|
InnerMessage = (string)info.GetValue(nameof(InnerMessage), InnerMessage.GetType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Loader;
|
||||||
|
|
||||||
namespace LibHac
|
namespace LibHac
|
||||||
{
|
{
|
||||||
|
@ -171,7 +172,7 @@ namespace LibHac
|
||||||
|
|
||||||
public class Ini1
|
public class Ini1
|
||||||
{
|
{
|
||||||
public Kip[] Kips { get; }
|
public KipReader[] Kips { get; }
|
||||||
|
|
||||||
public string Magic { get; }
|
public string Magic { get; }
|
||||||
public int Size { get; }
|
public int Size { get; }
|
||||||
|
@ -194,13 +195,15 @@ namespace LibHac
|
||||||
Size = reader.ReadInt32();
|
Size = reader.ReadInt32();
|
||||||
KipCount = reader.ReadInt32();
|
KipCount = reader.ReadInt32();
|
||||||
|
|
||||||
Kips = new Kip[KipCount];
|
Kips = new KipReader[KipCount];
|
||||||
int offset = 0x10;
|
int offset = 0x10;
|
||||||
|
|
||||||
for (int i = 0; i < KipCount; i++)
|
for (int i = 0; i < KipCount; i++)
|
||||||
{
|
{
|
||||||
Kips[i] = new Kip(Storage.Slice(offset));
|
Kips[i] = new KipReader();
|
||||||
offset += Kips[i].Size;
|
Kips[i].Initialize(Storage.Slice(offset)).ThrowIfFailure();
|
||||||
|
|
||||||
|
offset += Kips[i].GetUncompressedSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace LibHac.Loader
|
||||||
{
|
{
|
||||||
public class KipReader
|
public class KipReader
|
||||||
{
|
{
|
||||||
private IFile KipFile { get; set; }
|
private IStorage KipStorage { get; set; }
|
||||||
|
|
||||||
private KipHeader _header;
|
private KipHeader _header;
|
||||||
|
|
||||||
|
@ -34,21 +34,48 @@ namespace LibHac.Loader
|
||||||
public int AffinityMask => _header.AffinityMask;
|
public int AffinityMask => _header.AffinityMask;
|
||||||
public int StackSize => _header.StackSize;
|
public int StackSize => _header.StackSize;
|
||||||
|
|
||||||
public Result Initialize(IFile kipFile)
|
public Result Initialize(IStorage kipData)
|
||||||
{
|
{
|
||||||
if (kipFile is null)
|
if (kipData is null)
|
||||||
return ResultLibHac.NullArgument.Log();
|
return ResultLibHac.NullArgument.Log();
|
||||||
|
|
||||||
Result rc = kipFile.Read(out long bytesRead, 0, SpanHelpers.AsByteSpan(ref _header), ReadOption.None);
|
// Verify there's enough data to read the header
|
||||||
|
Result rc = kipData.GetSize(out long kipSize);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (bytesRead != Unsafe.SizeOf<KipHeader>())
|
if (kipSize < Unsafe.SizeOf<KipHeader>())
|
||||||
return ResultLibHac.InvalidKipFileSize.Log();
|
return ResultLibHac.InvalidKipFileSize.Log();
|
||||||
|
|
||||||
|
rc = kipData.Read(0, SpanHelpers.AsByteSpan(ref _header));
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (!_header.IsValid)
|
if (!_header.IsValid)
|
||||||
return ResultLibHac.InvalidKipMagic.Log();
|
return ResultLibHac.InvalidKipMagic.Log();
|
||||||
|
|
||||||
KipFile = kipFile;
|
KipStorage = kipData;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the raw input KIP file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="kipData">If the operation returns successfully, an <see cref="IStorage"/>
|
||||||
|
/// containing the KIP data.</param>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
public Result GetRawData(out IStorage kipData)
|
||||||
|
{
|
||||||
|
kipData = default;
|
||||||
|
|
||||||
|
int kipFileSize = GetFileSize();
|
||||||
|
|
||||||
|
Result rc = KipStorage.GetSize(out long inputFileSize);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
// Verify the input KIP file isn't truncated
|
||||||
|
if (inputFileSize < kipFileSize)
|
||||||
|
return ResultLibHac.InvalidKipFileSize.Log();
|
||||||
|
|
||||||
|
kipData = new SubStorage2(KipStorage, 0, kipFileSize);
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +97,18 @@ namespace LibHac.Loader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int GetFileSize()
|
||||||
|
{
|
||||||
|
int size = Unsafe.SizeOf<KipHeader>();
|
||||||
|
|
||||||
|
for (int i = 0; i < Segments.Length; i++)
|
||||||
|
{
|
||||||
|
size += Segments[i].FileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
public int GetUncompressedSize()
|
public int GetUncompressedSize()
|
||||||
{
|
{
|
||||||
int size = Unsafe.SizeOf<KipHeader>();
|
int size = Unsafe.SizeOf<KipHeader>();
|
||||||
|
@ -108,13 +147,17 @@ namespace LibHac.Loader
|
||||||
|
|
||||||
int offset = CalculateSegmentOffset((int)segment);
|
int offset = CalculateSegmentOffset((int)segment);
|
||||||
|
|
||||||
// Read the segment data.
|
// Verify the segment offset is in-range
|
||||||
rc = KipFile.Read(out long bytesRead, offset, buffer.Slice(0, segmentHeader.FileSize), ReadOption.None);
|
rc = KipStorage.GetSize(out long kipSize);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (bytesRead != segmentHeader.FileSize)
|
if (kipSize < offset + segmentHeader.FileSize)
|
||||||
return ResultLibHac.InvalidKipFileSize.Log();
|
return ResultLibHac.InvalidKipFileSize.Log();
|
||||||
|
|
||||||
|
// Read the segment data.
|
||||||
|
rc = KipStorage.Read(offset, buffer.Slice(0, segmentHeader.FileSize));
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Decompress if necessary.
|
// Decompress if necessary.
|
||||||
bool isCompressed = segment switch
|
bool isCompressed = segment switch
|
||||||
{
|
{
|
||||||
|
|
|
@ -69,7 +69,7 @@ namespace LibHac
|
||||||
: base(info, context)
|
: base(info, context)
|
||||||
{
|
{
|
||||||
Name = info.GetString(nameof(Name));
|
Name = info.GetString(nameof(Name));
|
||||||
Type = (KeyType)info.GetValue(nameof(Type), Type.GetType());
|
Type = (KeyType)(info.GetValue(nameof(Type), Type.GetType()) ?? default(KeyType));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
|
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace LibHac
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
[DebuggerDisplay("{" + nameof(ToStringWithName) + "(),nq}")]
|
[DebuggerDisplay("{" + nameof(ToStringWithName) + "(),nq}")]
|
||||||
public struct Result : IEquatable<Result>
|
public readonly struct Result : IEquatable<Result>
|
||||||
{
|
{
|
||||||
private const BaseType SuccessValue = default;
|
private const BaseType SuccessValue = default;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -218,7 +218,7 @@ namespace LibHac
|
||||||
/// <c>public static Result.Base SdCardAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 2000, 2499); }</c>
|
/// <c>public static Result.Base SdCardAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 2000, 2499); }</c>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DebuggerDisplay("{" + nameof(ToStringWithName) + "(),nq}")]
|
[DebuggerDisplay("{" + nameof(ToStringWithName) + "(),nq}")]
|
||||||
public struct Base
|
public readonly struct Base
|
||||||
{
|
{
|
||||||
private const int DescriptionEndBitsOffset = ReservedBitsOffset;
|
private const int DescriptionEndBitsOffset = ReservedBitsOffset;
|
||||||
private readonly ulong _value;
|
private readonly ulong _value;
|
||||||
|
|
|
@ -11,6 +11,7 @@ namespace LibHac.Sm
|
||||||
// isn't blocked waiting for something better.
|
// isn't blocked waiting for something better.
|
||||||
internal class ServiceManager
|
internal class ServiceManager
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||||
private Horizon Horizon { get; }
|
private Horizon Horizon { get; }
|
||||||
private Dictionary<ServiceName, object> Services { get; } = new Dictionary<ServiceName, object>();
|
private Dictionary<ServiceName, object> Services { get; } = new Dictionary<ServiceName, object>();
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ namespace LibHac.Sm
|
||||||
{
|
{
|
||||||
private const int MaxLength = 8;
|
private const int MaxLength = 8;
|
||||||
|
|
||||||
|
// The Name should always be assigned in the below Encode method
|
||||||
|
// ReSharper disable once UnassignedReadonlyField
|
||||||
public readonly ulong Name;
|
public readonly ulong Name;
|
||||||
|
|
||||||
public static ServiceName Encode(ReadOnlySpan<char> name)
|
public static ServiceName Encode(ReadOnlySpan<char> name)
|
||||||
|
|
|
@ -9,6 +9,7 @@ using LibHac.FsSystem;
|
||||||
using LibHac.FsSystem.NcaUtils;
|
using LibHac.FsSystem.NcaUtils;
|
||||||
using LibHac.FsSystem.Save;
|
using LibHac.FsSystem.Save;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
|
using LibHac.Ns;
|
||||||
|
|
||||||
namespace LibHac
|
namespace LibHac
|
||||||
{
|
{
|
||||||
|
@ -195,13 +196,16 @@ namespace LibHac
|
||||||
IFileSystem romfs = title.ControlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
IFileSystem romfs = title.ControlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||||
romfs.OpenFile(out IFile control, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
romfs.OpenFile(out IFile control, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
title.Control = new Nacp(control.AsStream());
|
using (control)
|
||||||
|
{
|
||||||
|
control.Read(out _, 0, title.Control.ByteSpan).ThrowIfFailure();
|
||||||
|
}
|
||||||
|
|
||||||
foreach (NacpDescription desc in title.Control.Descriptions)
|
foreach (ref ApplicationControlTitle desc in title.Control.Value.Titles)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(desc.Title))
|
if (!desc.Name.IsEmpty())
|
||||||
{
|
{
|
||||||
title.Name = desc.Title;
|
title.Name = desc.Name.ToString();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,7 +324,7 @@ namespace LibHac
|
||||||
public Cnmt Metadata { get; internal set; }
|
public Cnmt Metadata { get; internal set; }
|
||||||
|
|
||||||
public string Name { get; internal set; }
|
public string Name { get; internal set; }
|
||||||
public Nacp Control { get; internal set; }
|
public BlitStruct<ApplicationControlProperty> Control { get; } = new BlitStruct<ApplicationControlProperty>(1);
|
||||||
public SwitchFsNca MetaNca { get; internal set; }
|
public SwitchFsNca MetaNca { get; internal set; }
|
||||||
public SwitchFsNca MainNca { get; internal set; }
|
public SwitchFsNca MainNca { get; internal set; }
|
||||||
public SwitchFsNca ControlNca { get; internal set; }
|
public SwitchFsNca ControlNca { get; internal set; }
|
||||||
|
@ -339,7 +343,7 @@ namespace LibHac
|
||||||
|
|
||||||
public ulong TitleId { get; private set; }
|
public ulong TitleId { get; private set; }
|
||||||
public TitleVersion Version { get; private set; }
|
public TitleVersion Version { get; private set; }
|
||||||
public Nacp Nacp { get; private set; }
|
public BlitStruct<ApplicationControlProperty> Nacp { get; private set; } = new BlitStruct<ApplicationControlProperty>(1);
|
||||||
|
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
public string DisplayVersion { get; private set; }
|
public string DisplayVersion { get; private set; }
|
||||||
|
@ -374,14 +378,14 @@ namespace LibHac
|
||||||
{
|
{
|
||||||
Name = Patch.Name;
|
Name = Patch.Name;
|
||||||
Version = Patch.Version;
|
Version = Patch.Version;
|
||||||
DisplayVersion = Patch.Control?.DisplayVersion ?? "";
|
DisplayVersion = Patch.Control.Value.DisplayVersion.ToString();
|
||||||
Nacp = Patch.Control;
|
Nacp = Patch.Control;
|
||||||
}
|
}
|
||||||
else if (Main != null)
|
else if (Main != null)
|
||||||
{
|
{
|
||||||
Name = Main.Name;
|
Name = Main.Name;
|
||||||
Version = Main.Version;
|
Version = Main.Version;
|
||||||
DisplayVersion = Main.Control?.DisplayVersion ?? "";
|
DisplayVersion = Main.Control.Value.DisplayVersion.ToString();
|
||||||
Nacp = Main.Control;
|
Nacp = Main.Control;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using LibHac;
|
using LibHac;
|
||||||
using LibHac.Fs;
|
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.Loader;
|
using LibHac.Loader;
|
||||||
|
|
||||||
|
@ -10,7 +9,7 @@ namespace hactoolnet
|
||||||
{
|
{
|
||||||
public static void ProcessKip1(Context ctx)
|
public static void ProcessKip1(Context ctx)
|
||||||
{
|
{
|
||||||
using (var file = new LocalFile(ctx.Options.InFile, OpenMode.Read))
|
using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.Read))
|
||||||
{
|
{
|
||||||
var kip = new KipReader();
|
var kip = new KipReader();
|
||||||
kip.Initialize(file).ThrowIfFailure();
|
kip.Initialize(file).ThrowIfFailure();
|
||||||
|
@ -38,9 +37,13 @@ namespace hactoolnet
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(outDir);
|
Directory.CreateDirectory(outDir);
|
||||||
|
|
||||||
foreach (Kip kip in ini1.Kips)
|
foreach (KipReader kip in ini1.Kips)
|
||||||
{
|
{
|
||||||
kip.OpenRawFile().WriteAllBytes(Path.Combine(outDir, $"{kip.Header.Name}.kip1"));
|
var uncompressed = new byte[kip.GetUncompressedSize()];
|
||||||
|
|
||||||
|
kip.ReadUncompressedKip(uncompressed).ThrowIfFailure();
|
||||||
|
|
||||||
|
File.WriteAllBytes(Path.Combine(outDir, $"{kip.Name.ToString()}.kip1"), uncompressed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ using LibHac.Fs;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.FsSystem.NcaUtils;
|
using LibHac.FsSystem.NcaUtils;
|
||||||
using LibHac.FsSystem.Save;
|
using LibHac.FsSystem.Save;
|
||||||
|
using LibHac.Ns;
|
||||||
|
|
||||||
namespace hactoolnet
|
namespace hactoolnet
|
||||||
{
|
{
|
||||||
|
@ -241,7 +242,7 @@ namespace hactoolnet
|
||||||
title.Version?.ToString(),
|
title.Version?.ToString(),
|
||||||
title.Metadata?.Type.ToString(),
|
title.Metadata?.Type.ToString(),
|
||||||
Util.GetBytesReadable(title.GetSize()),
|
Util.GetBytesReadable(title.GetSize()),
|
||||||
title.Control?.DisplayVersion,
|
title.Control.Value.DisplayVersion.ToString(),
|
||||||
title.Name);
|
title.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,12 +284,17 @@ namespace hactoolnet
|
||||||
sb.AppendLine($"DLC: {Util.GetBytesReadable(app.AddOnContent.Sum(x => x.GetSize()))}");
|
sb.AppendLine($"DLC: {Util.GetBytesReadable(app.AddOnContent.Sum(x => x.GetSize()))}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app.Nacp?.UserTotalSaveDataSize > 0)
|
ref ApplicationControlProperty nacp = ref app.Nacp.Value;
|
||||||
sb.AppendLine($"User save: {Util.GetBytesReadable(app.Nacp.UserTotalSaveDataSize)}");
|
|
||||||
if (app.Nacp?.DeviceTotalSaveDataSize > 0)
|
long userTotalSaveDataSize = nacp.UserAccountSaveDataSize + nacp.UserAccountSaveDataJournalSize;
|
||||||
sb.AppendLine($"System save: {Util.GetBytesReadable(app.Nacp.DeviceTotalSaveDataSize)}");
|
long deviceTotalSaveDataSize = nacp.DeviceSaveDataSize + nacp.DeviceSaveDataJournalSize;
|
||||||
if (app.Nacp?.BcatDeliveryCacheStorageSize > 0)
|
|
||||||
sb.AppendLine($"BCAT save: {Util.GetBytesReadable(app.Nacp.BcatDeliveryCacheStorageSize)}");
|
if (userTotalSaveDataSize > 0)
|
||||||
|
sb.AppendLine($"User save: {Util.GetBytesReadable(userTotalSaveDataSize)}");
|
||||||
|
if (deviceTotalSaveDataSize > 0)
|
||||||
|
sb.AppendLine($"System save: {Util.GetBytesReadable(deviceTotalSaveDataSize)}");
|
||||||
|
if (nacp.BcatDeliveryCacheStorageSize > 0)
|
||||||
|
sb.AppendLine($"BCAT save: {Util.GetBytesReadable(nacp.BcatDeliveryCacheStorageSize)}");
|
||||||
|
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,10 @@ namespace hactoolnet
|
||||||
public void LogResult(Result result)
|
public void LogResult(Result result)
|
||||||
{
|
{
|
||||||
StackTrace st = GetStackTrace();
|
StackTrace st = GetStackTrace();
|
||||||
MethodBase method = st.GetFrame(0).GetMethod();
|
MethodBase method = st.GetFrame(0)?.GetMethod();
|
||||||
|
|
||||||
|
if (method is null)
|
||||||
|
return;
|
||||||
|
|
||||||
// This result from these functions is usually noise because they
|
// This result from these functions is usually noise because they
|
||||||
// are frequently used to detect if a file exists
|
// are frequently used to detect if a file exists
|
||||||
|
@ -90,7 +93,11 @@ namespace hactoolnet
|
||||||
|
|
||||||
private void PrintLogEntry(LogEntry entry)
|
private void PrintLogEntry(LogEntry entry)
|
||||||
{
|
{
|
||||||
MethodBase method = entry.StackTrace.GetFrame(0).GetMethod();
|
MethodBase method = entry.StackTrace.GetFrame(0)?.GetMethod();
|
||||||
|
|
||||||
|
if (method is null)
|
||||||
|
return;
|
||||||
|
|
||||||
string methodName = $"{method.DeclaringType?.FullName}.{method.Name}";
|
string methodName = $"{method.DeclaringType?.FullName}.{method.Name}";
|
||||||
|
|
||||||
bool printStackTrace = PrintStackTrace && !entry.IsConvertedResult;
|
bool printStackTrace = PrintStackTrace && !entry.IsConvertedResult;
|
||||||
|
@ -189,11 +196,22 @@ namespace hactoolnet
|
||||||
IsConvertedResult = isConverted;
|
IsConvertedResult = isConverted;
|
||||||
OriginalResult = originalResult;
|
OriginalResult = originalResult;
|
||||||
|
|
||||||
MethodBase method = stackTrace.GetFrame(0).GetMethod();
|
MethodBase method = stackTrace.GetFrame(0)?.GetMethod();
|
||||||
|
|
||||||
|
if (method is null)
|
||||||
|
{
|
||||||
|
CallingMethod = string.Empty;
|
||||||
|
StackTraceText = string.Empty;
|
||||||
|
LineNumber = 0;
|
||||||
|
TimesCalled = 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CallingMethod = $"{method.DeclaringType?.FullName}.{method.Name}";
|
CallingMethod = $"{method.DeclaringType?.FullName}.{method.Name}";
|
||||||
|
|
||||||
StackTraceText = stackTrace.ToString();
|
StackTraceText = stackTrace.ToString();
|
||||||
LineNumber = stackTrace.GetFrame(0).GetFileLineNumber();
|
LineNumber = stackTrace.GetFrame(0)?.GetFileLineNumber() ?? 0;
|
||||||
TimesCalled = 1;
|
TimesCalled = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,12 +23,15 @@ namespace hactoolnet
|
||||||
|
|
||||||
foreach (TypeInfo type in assembly.DefinedTypes.Where(x => x.Name.Contains("Result")))
|
foreach (TypeInfo type in assembly.DefinedTypes.Where(x => x.Name.Contains("Result")))
|
||||||
foreach (PropertyInfo property in type.DeclaredProperties
|
foreach (PropertyInfo property in type.DeclaredProperties
|
||||||
.Where(x => x.PropertyType == typeof(Result.Base) && x.GetMethod.IsStatic && x.SetMethod == null))
|
.Where(x => x.PropertyType == typeof(Result.Base) && x.GetMethod?.IsStatic == true && x.SetMethod == null))
|
||||||
{
|
{
|
||||||
Result value = ((Result.Base)property.GetValue(null, null)).Value;
|
object value = property.GetValue(null, null);
|
||||||
|
if (value is null) continue;
|
||||||
|
|
||||||
|
Result resultValue = ((Result.Base)value).Value;
|
||||||
string name = $"{type.Name}{property.Name}";
|
string name = $"{type.Name}{property.Name}";
|
||||||
|
|
||||||
dict[value] = name;
|
dict[resultValue] = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dict;
|
return dict;
|
||||||
|
|
|
@ -20,16 +20,16 @@ namespace LibHac.Tests.Fs
|
||||||
|
|
||||||
private class DirectorySaveDataFileSystemCreator : IReopenableFileSystemCreator
|
private class DirectorySaveDataFileSystemCreator : IReopenableFileSystemCreator
|
||||||
{
|
{
|
||||||
private IFileSystem _baseFileSystem { get; }
|
private IFileSystem BaseFileSystem { get; }
|
||||||
|
|
||||||
public DirectorySaveDataFileSystemCreator()
|
public DirectorySaveDataFileSystemCreator()
|
||||||
{
|
{
|
||||||
_baseFileSystem = new InMemoryFileSystem();
|
BaseFileSystem = new InMemoryFileSystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IFileSystem Create()
|
public IFileSystem Create()
|
||||||
{
|
{
|
||||||
DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, _baseFileSystem, true, true)
|
DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, BaseFileSystem, true, true)
|
||||||
.ThrowIfFailure();
|
.ThrowIfFailure();
|
||||||
|
|
||||||
return saveFs;
|
return saveFs;
|
||||||
|
|
|
@ -87,7 +87,7 @@ namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MountBis_InvalidPartition_ReturnsInvalidArgument()
|
public void MountBis_InvalidPartition_ReturnsInvalidArgument()
|
||||||
{
|
{
|
||||||
FileSystemClient fs = FileSystemServerFactory.CreateClient(out IFileSystem rootFs);
|
FileSystemClient fs = FileSystemServerFactory.CreateClient(out IFileSystem _);
|
||||||
|
|
||||||
Assert.Result(ResultFs.InvalidArgument, fs.MountBis("boot1".ToU8Span(), BisPartitionId.BootPartition1Root));
|
Assert.Result(ResultFs.InvalidArgument, fs.MountBis("boot1".ToU8Span(), BisPartitionId.BootPartition1Root));
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,15 @@ namespace LibHac.Tests
|
||||||
|
|
||||||
foreach (TypeInfo type in assembly.DefinedTypes.Where(x => x.Name.Contains("Result")))
|
foreach (TypeInfo type in assembly.DefinedTypes.Where(x => x.Name.Contains("Result")))
|
||||||
foreach (PropertyInfo property in type.DeclaredProperties
|
foreach (PropertyInfo property in type.DeclaredProperties
|
||||||
.Where(x => x.PropertyType == typeof(Result.Base) && x.GetMethod.IsStatic && x.SetMethod == null))
|
.Where(x => x.PropertyType == typeof(Result.Base) && x.GetMethod?.IsStatic == true && x.SetMethod == null))
|
||||||
{
|
{
|
||||||
Result value = ((Result.Base)property.GetValue(null, null)).Value;
|
object value = property.GetValue(null, null);
|
||||||
|
if (value is null) continue;
|
||||||
|
|
||||||
|
Result resultValue = ((Result.Base)value).Value;
|
||||||
string name = $"{type.Name}{property.Name}";
|
string name = $"{type.Name}{property.Name}";
|
||||||
|
|
||||||
dict[value] = name;
|
dict[resultValue] = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dict;
|
return dict;
|
||||||
|
|
Loading…
Reference in a new issue