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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,10 +21,16 @@ namespace LibHac.Bcat
|
|||
public static Result.Base NotFound => new Result.Base(ModuleBcat, 2);
|
||||
/// <summary>Error code: 2122-0003; Inner value: 0x67a</summary>
|
||||
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>
|
||||
public static Result.Base AlreadyOpen => new Result.Base(ModuleBcat, 6);
|
||||
/// <summary>Error code: 2122-0007; Inner value: 0xe7a</summary>
|
||||
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>
|
||||
public static Result.Base ServiceOpenLimitReached => new Result.Base(ModuleBcat, 9);
|
||||
/// <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);
|
||||
/// <summary>Error code: 2122-0080; Inner value: 0xa07a</summary>
|
||||
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>
|
||||
public static Result.Base PermissionDenied => new Result.Base(ModuleBcat, 90);
|
||||
/// <summary>Error code: 2122-0091; Inner value: 0xb67a</summary>
|
||||
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>
|
||||
public static Result.Base InvalidDeliveryCacheStorageFile => new Result.Base(ModuleBcat, 204);
|
||||
/// <summary>Error code: 2122-0205; Inner value: 0x19a7a</summary>
|
||||
|
|
|
@ -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<byte> _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;
|
||||
/// <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
|
||||
{
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
public struct U8String
|
||||
public readonly struct U8String
|
||||
{
|
||||
private readonly byte[] _buffer;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/// <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;
|
||||
|
||||
/// <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.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;
|
||||
|
|
|
@ -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<CacheBlock>(new CacheBlock {Buffer = new byte[BlockSize], Index = -1});
|
||||
|
||||
FlushBlock(node.Value);
|
||||
|
||||
CacheBlock block = node.Value;
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace LibHac
|
|||
{
|
||||
public class HorizonClient
|
||||
{
|
||||
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||
private Horizon Horizon { get; }
|
||||
|
||||
private Lazy<ArpClient> ArpLazy { get; }
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<KipHeader>())
|
||||
if (kipSize < Unsafe.SizeOf<KipHeader>())
|
||||
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;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
{
|
||||
int size = Unsafe.SizeOf<KipHeader>();
|
||||
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace LibHac
|
|||
/// </summary>
|
||||
[Serializable]
|
||||
[DebuggerDisplay("{" + nameof(ToStringWithName) + "(),nq}")]
|
||||
public struct Result : IEquatable<Result>
|
||||
public readonly struct Result : IEquatable<Result>
|
||||
{
|
||||
private const BaseType SuccessValue = default;
|
||||
/// <summary>
|
||||
|
@ -218,7 +218,7 @@ namespace LibHac
|
|||
/// <c>public static Result.Base SdCardAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 2000, 2499); }</c>
|
||||
/// </remarks>
|
||||
[DebuggerDisplay("{" + nameof(ToStringWithName) + "(),nq}")]
|
||||
public struct Base
|
||||
public readonly struct Base
|
||||
{
|
||||
private const int DescriptionEndBitsOffset = ReservedBitsOffset;
|
||||
private readonly ulong _value;
|
||||
|
|
|
@ -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<ServiceName, object> Services { get; } = new Dictionary<ServiceName, object>();
|
||||
|
||||
|
|
|
@ -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<char> name)
|
||||
|
|
|
@ -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<ApplicationControlProperty> Control { get; } = new BlitStruct<ApplicationControlProperty>(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<ApplicationControlProperty> Nacp { get; private set; } = new BlitStruct<ApplicationControlProperty>(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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue