diff --git a/src/LibHac/Bcat/Detail/Ipc/IServiceCreator.cs b/src/LibHac/Bcat/Detail/Ipc/IServiceCreator.cs
index 015d39ba..0b7c136a 100644
--- a/src/LibHac/Bcat/Detail/Ipc/IServiceCreator.cs
+++ b/src/LibHac/Bcat/Detail/Ipc/IServiceCreator.cs
@@ -1,6 +1,4 @@
-using LibHac.Ncm;
-
-namespace LibHac.Bcat.Detail.Ipc
+namespace LibHac.Bcat.Detail.Ipc
{
public interface IServiceCreator
{
diff --git a/src/LibHac/Bcat/Detail/Service/Core/DeliveryCacheFileMetaEntry.cs b/src/LibHac/Bcat/Detail/Service/Core/DeliveryCacheFileMetaEntry.cs
index 53d2392f..2ffa897a 100644
--- a/src/LibHac/Bcat/Detail/Service/Core/DeliveryCacheFileMetaEntry.cs
+++ b/src/LibHac/Bcat/Detail/Service/Core/DeliveryCacheFileMetaEntry.cs
@@ -6,7 +6,8 @@ namespace LibHac.Bcat.Detail.Service.Core
internal struct DeliveryCacheFileMetaEntry
{
[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;
}
}
diff --git a/src/LibHac/Bcat/ResultBcat.cs b/src/LibHac/Bcat/ResultBcat.cs
index 9a5fd6b1..c91295c1 100644
--- a/src/LibHac/Bcat/ResultBcat.cs
+++ b/src/LibHac/Bcat/ResultBcat.cs
@@ -21,10 +21,16 @@ namespace LibHac.Bcat
public static Result.Base NotFound => new Result.Base(ModuleBcat, 2);
/// Error code: 2122-0003; Inner value: 0x67a
public static Result.Base TargetLocked => new Result.Base(ModuleBcat, 3);
+ /// Error code: 2122-0004; Inner value: 0x87a
+ public static Result.Base TargetAlreadyMounted => new Result.Base(ModuleBcat, 4);
+ /// Error code: 2122-0005; Inner value: 0xa7a
+ public static Result.Base TargetNotMounted => new Result.Base(ModuleBcat, 5);
/// Error code: 2122-0006; Inner value: 0xc7a
public static Result.Base AlreadyOpen => new Result.Base(ModuleBcat, 6);
/// Error code: 2122-0007; Inner value: 0xe7a
public static Result.Base NotOpen => new Result.Base(ModuleBcat, 7);
+ /// Error code: 2122-0008; Inner value: 0x107a
+ public static Result.Base InternetRequestDenied => new Result.Base(ModuleBcat, 8);
/// Error code: 2122-0009; Inner value: 0x127a
public static Result.Base ServiceOpenLimitReached => new Result.Base(ModuleBcat, 9);
/// Error code: 2122-0010; Inner value: 0x147a
@@ -33,10 +39,14 @@ namespace LibHac.Bcat
public static Result.Base NetworkServiceAccountNotAvailable => new Result.Base(ModuleBcat, 31);
/// Error code: 2122-0080; Inner value: 0xa07a
public static Result.Base PassphrasePathNotFound => new Result.Base(ModuleBcat, 80);
+ /// Error code: 2122-0081; Inner value: 0xa27a
+ public static Result.Base DataVerificationFailed => new Result.Base(ModuleBcat, 81);
/// Error code: 2122-0090; Inner value: 0xb47a
public static Result.Base PermissionDenied => new Result.Base(ModuleBcat, 90);
/// Error code: 2122-0091; Inner value: 0xb67a
public static Result.Base AllocationFailed => new Result.Base(ModuleBcat, 91);
+ /// Error code: 2122-0098; Inner value: 0xc47a
+ public static Result.Base InvalidOperation => new Result.Base(ModuleBcat, 98);
/// Error code: 2122-0204; Inner value: 0x1987a
public static Result.Base InvalidDeliveryCacheStorageFile => new Result.Base(ModuleBcat, 204);
/// Error code: 2122-0205; Inner value: 0x19a7a
diff --git a/src/LibHac/Common/U8SpanMutable.cs b/src/LibHac/Common/U8SpanMutable.cs
index c89d5c34..418fdccc 100644
--- a/src/LibHac/Common/U8SpanMutable.cs
+++ b/src/LibHac/Common/U8SpanMutable.cs
@@ -1,11 +1,13 @@
using System;
using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Text;
namespace LibHac.Common
{
[DebuggerDisplay("{ToString()}")]
- public ref struct U8SpanMutable
+ public readonly ref struct U8SpanMutable
{
private readonly Span _buffer;
@@ -28,6 +30,29 @@ namespace LibHac.Common
_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)
{
return new U8SpanMutable(_buffer.Slice(start));
@@ -56,6 +81,18 @@ namespace LibHac.Common
return new U8StringMutable(_buffer.ToArray());
}
- public bool IsNull() => _buffer == default;
+ ///
+ /// Checks if the has no buffer.
+ ///
+ /// if the span has no buffer.
+ /// Otherwise, .
+ public bool IsNull() => _buffer.IsEmpty;
+
+ ///
+ /// Checks if the has no buffer or begins with a null terminator.
+ ///
+ /// if the span has no buffer or begins with a null terminator.
+ /// Otherwise, .
+ public bool IsEmpty() => _buffer.IsEmpty || MemoryMarshal.GetReference(_buffer) == 0;
}
}
diff --git a/src/LibHac/Common/U8String.cs b/src/LibHac/Common/U8String.cs
index c41083bb..56d60eaf 100644
--- a/src/LibHac/Common/U8String.cs
+++ b/src/LibHac/Common/U8String.cs
@@ -5,7 +5,7 @@ using System.Text;
namespace LibHac.Common
{
[DebuggerDisplay("{ToString()}")]
- public struct U8String
+ public readonly struct U8String
{
private readonly byte[] _buffer;
diff --git a/src/LibHac/Common/U8StringMutable.cs b/src/LibHac/Common/U8StringMutable.cs
index 2a0d659b..c808014f 100644
--- a/src/LibHac/Common/U8StringMutable.cs
+++ b/src/LibHac/Common/U8StringMutable.cs
@@ -5,7 +5,7 @@ using System.Text;
namespace LibHac.Common
{
[DebuggerDisplay("{ToString()}")]
- public struct U8StringMutable
+ public readonly struct U8StringMutable
{
private readonly byte[] _buffer;
@@ -53,6 +53,18 @@ namespace LibHac.Common
return StringUtils.Utf8ZToString(_buffer);
}
+ ///
+ /// Checks if the has no buffer.
+ ///
+ /// if the string has no buffer.
+ /// Otherwise, .
public bool IsNull() => _buffer == null;
+
+ ///
+ /// Checks if the has no buffer or begins with a null terminator.
+ ///
+ /// if the string has no buffer or begins with a null terminator.
+ /// Otherwise, .
+ public bool IsEmpty() => _buffer == null || _buffer.Length < 1 || _buffer[0] == 0;
}
}
diff --git a/src/LibHac/Fs/InMemoryFileSystem.cs b/src/LibHac/Fs/InMemoryFileSystem.cs
index 0b793424..048e5dbf 100644
--- a/src/LibHac/Fs/InMemoryFileSystem.cs
+++ b/src/LibHac/Fs/InMemoryFileSystem.cs
@@ -265,7 +265,13 @@ namespace LibHac.Fs
entry.Type = DirectoryEntryType.File;
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++;
CurrentFile = CurrentFile.Next;
diff --git a/src/LibHac/FsSystem/CachedStorage.cs b/src/LibHac/FsSystem/CachedStorage.cs
index 956ef1cf..bba36958 100644
--- a/src/LibHac/FsSystem/CachedStorage.cs
+++ b/src/LibHac/FsSystem/CachedStorage.cs
@@ -145,10 +145,19 @@ namespace LibHac.FsSystem
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;
}
- node = Blocks.Last;
+ // An inactive node shouldn't be null, but we'll fix it if it is anyway
+ node = Blocks.Last ??
+ new LinkedListNode(new CacheBlock {Buffer = new byte[BlockSize], Index = -1});
+
FlushBlock(node.Value);
CacheBlock block = node.Value;
diff --git a/src/LibHac/FsSystem/StorageExtensions.cs b/src/LibHac/FsSystem/StorageExtensions.cs
index 3deebdf5..107b5a80 100644
--- a/src/LibHac/FsSystem/StorageExtensions.cs
+++ b/src/LibHac/FsSystem/StorageExtensions.cs
@@ -8,19 +8,25 @@ namespace LibHac.FsSystem
{
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)
{
ValidateStorageParameters(buffer, offset, count, bufferOffset);
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)
{
ValidateStorageParameters(buffer, offset, count, bufferOffset);
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)
+ // ReSharper restore ParameterOnlyUsedForPreconditionCheck.Local
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), "Argument must be non-negative.");
diff --git a/src/LibHac/HorizonClient.cs b/src/LibHac/HorizonClient.cs
index 16207b76..0801e6d6 100644
--- a/src/LibHac/HorizonClient.cs
+++ b/src/LibHac/HorizonClient.cs
@@ -7,6 +7,7 @@ namespace LibHac
{
public class HorizonClient
{
+ // ReSharper disable once UnusedAutoPropertyAccessor.Local
private Horizon Horizon { get; }
private Lazy ArpLazy { get; }
diff --git a/src/LibHac/HorizonResultException.cs b/src/LibHac/HorizonResultException.cs
index 209176bb..8beeabb6 100644
--- a/src/LibHac/HorizonResultException.cs
+++ b/src/LibHac/HorizonResultException.cs
@@ -63,8 +63,8 @@ namespace LibHac
protected HorizonResultException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
- InternalResultValue = (Result)info.GetValue(nameof(InternalResultValue), InternalResultValue.GetType());
- ResultValue = (Result)info.GetValue(nameof(ResultValue), ResultValue.GetType());
+ InternalResultValue = (Result)(info.GetValue(nameof(InternalResultValue), InternalResultValue.GetType()) ?? default(Result));
+ ResultValue = (Result)(info.GetValue(nameof(ResultValue), ResultValue.GetType()) ?? default(Result));
InnerMessage = (string)info.GetValue(nameof(InnerMessage), InnerMessage.GetType());
}
diff --git a/src/LibHac/Kip.cs b/src/LibHac/Kip.cs
index ceaf5f55..8e7c89be 100644
--- a/src/LibHac/Kip.cs
+++ b/src/LibHac/Kip.cs
@@ -2,6 +2,7 @@
using System.IO;
using LibHac.Fs;
using LibHac.FsSystem;
+using LibHac.Loader;
namespace LibHac
{
@@ -171,7 +172,7 @@ namespace LibHac
public class Ini1
{
- public Kip[] Kips { get; }
+ public KipReader[] Kips { get; }
public string Magic { get; }
public int Size { get; }
@@ -194,13 +195,15 @@ namespace LibHac
Size = reader.ReadInt32();
KipCount = reader.ReadInt32();
- Kips = new Kip[KipCount];
+ Kips = new KipReader[KipCount];
int offset = 0x10;
for (int i = 0; i < KipCount; i++)
{
- Kips[i] = new Kip(Storage.Slice(offset));
- offset += Kips[i].Size;
+ Kips[i] = new KipReader();
+ Kips[i].Initialize(Storage.Slice(offset)).ThrowIfFailure();
+
+ offset += Kips[i].GetUncompressedSize();
}
}
}
diff --git a/src/LibHac/Loader/KipReader.cs b/src/LibHac/Loader/KipReader.cs
index 649d7f00..708a2c25 100644
--- a/src/LibHac/Loader/KipReader.cs
+++ b/src/LibHac/Loader/KipReader.cs
@@ -10,7 +10,7 @@ namespace LibHac.Loader
{
public class KipReader
{
- private IFile KipFile { get; set; }
+ private IStorage KipStorage { get; set; }
private KipHeader _header;
@@ -34,21 +34,48 @@ namespace LibHac.Loader
public int AffinityMask => _header.AffinityMask;
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();
- 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 (bytesRead != Unsafe.SizeOf())
+ if (kipSize < Unsafe.SizeOf())
return ResultLibHac.InvalidKipFileSize.Log();
+ rc = kipData.Read(0, SpanHelpers.AsByteSpan(ref _header));
+ if (rc.IsFailure()) return rc;
+
if (!_header.IsValid)
return ResultLibHac.InvalidKipMagic.Log();
- KipFile = kipFile;
+ KipStorage = kipData;
+ return Result.Success;
+ }
+
+ ///
+ /// Gets the raw input KIP file.
+ ///
+ /// If the operation returns successfully, an
+ /// containing the KIP data.
+ /// The of the operation.
+ 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;
}
@@ -70,6 +97,18 @@ namespace LibHac.Loader
}
}
+ private int GetFileSize()
+ {
+ int size = Unsafe.SizeOf();
+
+ for (int i = 0; i < Segments.Length; i++)
+ {
+ size += Segments[i].FileSize;
+ }
+
+ return size;
+ }
+
public int GetUncompressedSize()
{
int size = Unsafe.SizeOf();
@@ -108,13 +147,17 @@ namespace LibHac.Loader
int offset = CalculateSegmentOffset((int)segment);
- // Read the segment data.
- rc = KipFile.Read(out long bytesRead, offset, buffer.Slice(0, segmentHeader.FileSize), ReadOption.None);
+ // Verify the segment offset is in-range
+ rc = KipStorage.GetSize(out long kipSize);
if (rc.IsFailure()) return rc;
- if (bytesRead != segmentHeader.FileSize)
+ if (kipSize < offset + segmentHeader.FileSize)
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.
bool isCompressed = segment switch
{
diff --git a/src/LibHac/MissingKeyException.cs b/src/LibHac/MissingKeyException.cs
index 806785c9..f2588d81 100644
--- a/src/LibHac/MissingKeyException.cs
+++ b/src/LibHac/MissingKeyException.cs
@@ -69,7 +69,7 @@ namespace LibHac
: base(info, context)
{
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)
diff --git a/src/LibHac/Result.cs b/src/LibHac/Result.cs
index 660e4be1..830a7ffc 100644
--- a/src/LibHac/Result.cs
+++ b/src/LibHac/Result.cs
@@ -11,7 +11,7 @@ namespace LibHac
///
[Serializable]
[DebuggerDisplay("{" + nameof(ToStringWithName) + "(),nq}")]
- public struct Result : IEquatable
+ public readonly struct Result : IEquatable
{
private const BaseType SuccessValue = default;
///
@@ -218,7 +218,7 @@ namespace LibHac
/// public static Result.Base SdCardAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 2000, 2499); }
///
[DebuggerDisplay("{" + nameof(ToStringWithName) + "(),nq}")]
- public struct Base
+ public readonly struct Base
{
private const int DescriptionEndBitsOffset = ReservedBitsOffset;
private readonly ulong _value;
diff --git a/src/LibHac/Sm/ServiceManager.cs b/src/LibHac/Sm/ServiceManager.cs
index 9f37c8d2..7f31d592 100644
--- a/src/LibHac/Sm/ServiceManager.cs
+++ b/src/LibHac/Sm/ServiceManager.cs
@@ -11,6 +11,7 @@ namespace LibHac.Sm
// isn't blocked waiting for something better.
internal class ServiceManager
{
+ // ReSharper disable once UnusedAutoPropertyAccessor.Local
private Horizon Horizon { get; }
private Dictionary Services { get; } = new Dictionary();
diff --git a/src/LibHac/Sm/ServiceName.cs b/src/LibHac/Sm/ServiceName.cs
index 7c3deb3a..be92af43 100644
--- a/src/LibHac/Sm/ServiceName.cs
+++ b/src/LibHac/Sm/ServiceName.cs
@@ -9,6 +9,8 @@ namespace LibHac.Sm
{
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 static ServiceName Encode(ReadOnlySpan name)
diff --git a/src/LibHac/SwitchFs.cs b/src/LibHac/SwitchFs.cs
index 1d1af95c..565403be 100644
--- a/src/LibHac/SwitchFs.cs
+++ b/src/LibHac/SwitchFs.cs
@@ -9,6 +9,7 @@ using LibHac.FsSystem;
using LibHac.FsSystem.NcaUtils;
using LibHac.FsSystem.Save;
using LibHac.Ncm;
+using LibHac.Ns;
namespace LibHac
{
@@ -195,13 +196,16 @@ namespace LibHac
IFileSystem romfs = title.ControlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
romfs.OpenFile(out IFile control, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
- title.Control = new Nacp(control.AsStream());
-
- foreach (NacpDescription desc in title.Control.Descriptions)
+ using (control)
{
- if (!string.IsNullOrWhiteSpace(desc.Title))
+ control.Read(out _, 0, title.Control.ByteSpan).ThrowIfFailure();
+ }
+
+ foreach (ref ApplicationControlTitle desc in title.Control.Value.Titles)
+ {
+ if (!desc.Name.IsEmpty())
{
- title.Name = desc.Title;
+ title.Name = desc.Name.ToString();
break;
}
}
@@ -320,7 +324,7 @@ namespace LibHac
public Cnmt Metadata { get; internal set; }
public string Name { get; internal set; }
- public Nacp Control { get; internal set; }
+ public BlitStruct Control { get; } = new BlitStruct(1);
public SwitchFsNca MetaNca { get; internal set; }
public SwitchFsNca MainNca { get; internal set; }
public SwitchFsNca ControlNca { get; internal set; }
@@ -339,7 +343,7 @@ namespace LibHac
public ulong TitleId { get; private set; }
public TitleVersion Version { get; private set; }
- public Nacp Nacp { get; private set; }
+ public BlitStruct Nacp { get; private set; } = new BlitStruct(1);
public string Name { get; private set; }
public string DisplayVersion { get; private set; }
@@ -374,14 +378,14 @@ namespace LibHac
{
Name = Patch.Name;
Version = Patch.Version;
- DisplayVersion = Patch.Control?.DisplayVersion ?? "";
+ DisplayVersion = Patch.Control.Value.DisplayVersion.ToString();
Nacp = Patch.Control;
}
else if (Main != null)
{
Name = Main.Name;
Version = Main.Version;
- DisplayVersion = Main.Control?.DisplayVersion ?? "";
+ DisplayVersion = Main.Control.Value.DisplayVersion.ToString();
Nacp = Main.Control;
}
else
diff --git a/src/hactoolnet/ProcessKip.cs b/src/hactoolnet/ProcessKip.cs
index 1b36225c..17b99902 100644
--- a/src/hactoolnet/ProcessKip.cs
+++ b/src/hactoolnet/ProcessKip.cs
@@ -1,6 +1,5 @@
using System.IO;
using LibHac;
-using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.Loader;
@@ -10,7 +9,7 @@ namespace hactoolnet
{
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();
kip.Initialize(file).ThrowIfFailure();
@@ -38,9 +37,13 @@ namespace hactoolnet
{
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);
}
}
}
diff --git a/src/hactoolnet/ProcessSwitchFs.cs b/src/hactoolnet/ProcessSwitchFs.cs
index b60841a3..637848cd 100644
--- a/src/hactoolnet/ProcessSwitchFs.cs
+++ b/src/hactoolnet/ProcessSwitchFs.cs
@@ -9,6 +9,7 @@ using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.FsSystem.NcaUtils;
using LibHac.FsSystem.Save;
+using LibHac.Ns;
namespace hactoolnet
{
@@ -241,7 +242,7 @@ namespace hactoolnet
title.Version?.ToString(),
title.Metadata?.Type.ToString(),
Util.GetBytesReadable(title.GetSize()),
- title.Control?.DisplayVersion,
+ title.Control.Value.DisplayVersion.ToString(),
title.Name);
}
@@ -283,12 +284,17 @@ namespace hactoolnet
sb.AppendLine($"DLC: {Util.GetBytesReadable(app.AddOnContent.Sum(x => x.GetSize()))}");
}
- if (app.Nacp?.UserTotalSaveDataSize > 0)
- sb.AppendLine($"User save: {Util.GetBytesReadable(app.Nacp.UserTotalSaveDataSize)}");
- if (app.Nacp?.DeviceTotalSaveDataSize > 0)
- sb.AppendLine($"System save: {Util.GetBytesReadable(app.Nacp.DeviceTotalSaveDataSize)}");
- if (app.Nacp?.BcatDeliveryCacheStorageSize > 0)
- sb.AppendLine($"BCAT save: {Util.GetBytesReadable(app.Nacp.BcatDeliveryCacheStorageSize)}");
+ ref ApplicationControlProperty nacp = ref app.Nacp.Value;
+
+ long userTotalSaveDataSize = nacp.UserAccountSaveDataSize + nacp.UserAccountSaveDataJournalSize;
+ long deviceTotalSaveDataSize = nacp.DeviceSaveDataSize + nacp.DeviceSaveDataJournalSize;
+
+ 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();
}
diff --git a/src/hactoolnet/ResultLogger.cs b/src/hactoolnet/ResultLogger.cs
index 2ffe89de..ea9cec34 100644
--- a/src/hactoolnet/ResultLogger.cs
+++ b/src/hactoolnet/ResultLogger.cs
@@ -37,7 +37,10 @@ namespace hactoolnet
public void LogResult(Result result)
{
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
// are frequently used to detect if a file exists
@@ -90,7 +93,11 @@ namespace hactoolnet
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}";
bool printStackTrace = PrintStackTrace && !entry.IsConvertedResult;
@@ -189,11 +196,22 @@ namespace hactoolnet
IsConvertedResult = isConverted;
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}";
StackTraceText = stackTrace.ToString();
- LineNumber = stackTrace.GetFrame(0).GetFileLineNumber();
+ LineNumber = stackTrace.GetFrame(0)?.GetFileLineNumber() ?? 0;
TimesCalled = 1;
}
diff --git a/src/hactoolnet/ResultNameResolver.cs b/src/hactoolnet/ResultNameResolver.cs
index ae2e2d4f..47f5bca1 100644
--- a/src/hactoolnet/ResultNameResolver.cs
+++ b/src/hactoolnet/ResultNameResolver.cs
@@ -23,12 +23,15 @@ namespace hactoolnet
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))
+ .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}";
- dict[value] = name;
+ dict[resultValue] = name;
}
return dict;
diff --git a/tests/LibHac.Tests/Fs/DirectorySaveDataFileSystemTests.cs b/tests/LibHac.Tests/Fs/DirectorySaveDataFileSystemTests.cs
index 10014f0e..87e8eb74 100644
--- a/tests/LibHac.Tests/Fs/DirectorySaveDataFileSystemTests.cs
+++ b/tests/LibHac.Tests/Fs/DirectorySaveDataFileSystemTests.cs
@@ -20,16 +20,16 @@ namespace LibHac.Tests.Fs
private class DirectorySaveDataFileSystemCreator : IReopenableFileSystemCreator
{
- private IFileSystem _baseFileSystem { get; }
+ private IFileSystem BaseFileSystem { get; }
public DirectorySaveDataFileSystemCreator()
{
- _baseFileSystem = new InMemoryFileSystem();
+ BaseFileSystem = new InMemoryFileSystem();
}
public IFileSystem Create()
{
- DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, _baseFileSystem, true, true)
+ DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, BaseFileSystem, true, true)
.ThrowIfFailure();
return saveFs;
diff --git a/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/Bis.cs b/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/Bis.cs
index 9c41f8e2..c15f1be9 100644
--- a/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/Bis.cs
+++ b/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/Bis.cs
@@ -87,7 +87,7 @@ namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
[Fact]
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));
}
diff --git a/tests/LibHac.Tests/ResultNameResolver.cs b/tests/LibHac.Tests/ResultNameResolver.cs
index 3991d1a5..f133ddd6 100644
--- a/tests/LibHac.Tests/ResultNameResolver.cs
+++ b/tests/LibHac.Tests/ResultNameResolver.cs
@@ -22,12 +22,15 @@ namespace LibHac.Tests
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))
+ .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}";
- dict[value] = name;
+ dict[resultValue] = name;
}
return dict;