From b5dabe78f5635b9bc17df255f3edb83b940547ae Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sat, 10 Oct 2020 20:17:07 -0700 Subject: [PATCH] Replace hex string converter and move StringUtils --- .../Core/DeliveryCacheFileMetaAccessor.cs | 1 + .../Service/DeliveryCacheStorageService.cs | 2 +- src/LibHac/Bcat/Digest.cs | 1 + src/LibHac/Bcat/DirectoryName.cs | 1 + src/LibHac/Bcat/FileName.cs | 1 + src/LibHac/Boot/KeyBlob.cs | 1 + src/LibHac/Boot/Package1.cs | 1 + src/LibHac/Common/Buffer.cs | 1 + src/LibHac/Common/Id128.cs | 1 + src/LibHac/Common/Key128.cs | 1 + src/LibHac/Common/Keys/ExternalKeyReader.cs | 9 +- src/LibHac/Common/Keys/ExternalKeyWriter.cs | 1 + src/LibHac/Common/Keys/KeyInfo.cs | 3 +- src/LibHac/Common/PathBuilder.cs | 1 + src/LibHac/Common/U8Span.cs | 1 + src/LibHac/Common/U8SpanMutable.cs | 1 + src/LibHac/Common/U8String.cs | 1 + src/LibHac/Common/U8StringBuilder.cs | 1 + src/LibHac/Common/U8StringMutable.cs | 1 + src/LibHac/Crypto/KeyTypes.cs | 1 + src/LibHac/Fs/FileSystemClient.cs | 1 + src/LibHac/Fs/InMemoryFileSystem.cs | 1 + src/LibHac/Fs/RightsId.cs | 1 + src/LibHac/Fs/Shim/Bis.cs | 1 + src/LibHac/Fs/Shim/ContentStorage.cs | 1 + src/LibHac/Fs/Shim/Host.cs | 1 + src/LibHac/FsSrv/FileSystemProxy.cs | 1 + src/LibHac/FsSrv/FileSystemProxyCore.cs | 1 + src/LibHac/FsSrv/PathNormalizer.cs | 1 + src/LibHac/FsSrv/Sf/FspPath.cs | 1 + src/LibHac/FsSystem/AesXtsDirectory.cs | 3 +- src/LibHac/FsSystem/ConcatenationDirectory.cs | 5 +- .../FsSystem/ConcatenationFileSystem.cs | 1 + .../FsSystem/DirectorySaveDataFileSystem.cs | 1 + src/LibHac/FsSystem/DirectoryUtils.cs | 1 + src/LibHac/FsSystem/FileSystemExtensions.cs | 1 + src/LibHac/FsSystem/FsPath.cs | 1 + src/LibHac/FsSystem/LayeredFileSystem.cs | 1 + src/LibHac/FsSystem/LocalDirectory.cs | 4 +- src/LibHac/FsSystem/NcaUtils/NcaHeader.cs | 1 + src/LibHac/FsSystem/PartitionDirectory.cs | 2 +- .../FsSystem/PartitionFileSystemCore.cs | 1 + .../FsSystem/PartitionFileSystemMetaCore.cs | 1 + src/LibHac/FsSystem/PathTools.cs | 1 + .../RomFs/HierarchicalRomFileTable.cs | 13 +- src/LibHac/FsSystem/RomFs/RomFsDirectory.cs | 2 +- .../Save/HierarchicalSaveFileTable.cs | 5 +- src/LibHac/FsSystem/Save/SaveDataDirectory.cs | 2 +- src/LibHac/FsSystem/Save/SaveFsList.cs | 2 +- src/LibHac/FsSystem/SubdirectoryFileSystem.cs | 1 + src/LibHac/FsSystem/Utility.cs | 1 + src/LibHac/Kvdb/BoundedString.cs | 1 + src/LibHac/ResultNameResolver.cs | 1 + src/LibHac/Sm/ServiceName.cs | 1 + src/LibHac/SwitchFs.cs | 1 + src/LibHac/Util/Impl/HexConverter.cs | 233 ++++++++++++++++++ src/LibHac/{Common => Util}/StringUtils.cs | 79 +++++- src/LibHac/Utilities.cs | 162 ------------ src/hactoolnet/ProcessNax0.cs | 3 +- src/hactoolnet/ProcessPfs.cs | 1 + src/hactoolnet/Program.cs | 1 + tests/LibHac.Tests/AesCmac.cs | 1 + tests/LibHac.Tests/AesXts.cs | 1 + tests/LibHac.Tests/CryptoTests/RspReader.cs | 1 + .../IFileSystemTests.IDirectory.cs | 1 + .../LibHac.Tests/Fs/LayeredFileSystemTests.cs | 1 + tests/LibHac.Tests/Fs/PathToolTests.cs | 1 + tests/LibHac.Tests/PathToolsTests.cs | 1 + 68 files changed, 394 insertions(+), 187 deletions(-) create mode 100644 src/LibHac/Util/Impl/HexConverter.cs rename src/LibHac/{Common => Util}/StringUtils.cs (66%) diff --git a/src/LibHac/Bcat/Detail/Service/Core/DeliveryCacheFileMetaAccessor.cs b/src/LibHac/Bcat/Detail/Service/Core/DeliveryCacheFileMetaAccessor.cs index da3af499..dc0c6819 100644 --- a/src/LibHac/Bcat/Detail/Service/Core/DeliveryCacheFileMetaAccessor.cs +++ b/src/LibHac/Bcat/Detail/Service/Core/DeliveryCacheFileMetaAccessor.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Fs; +using LibHac.Util; namespace LibHac.Bcat.Detail.Service.Core { diff --git a/src/LibHac/Bcat/Detail/Service/DeliveryCacheStorageService.cs b/src/LibHac/Bcat/Detail/Service/DeliveryCacheStorageService.cs index 27699454..65aee83f 100644 --- a/src/LibHac/Bcat/Detail/Service/DeliveryCacheStorageService.cs +++ b/src/LibHac/Bcat/Detail/Service/DeliveryCacheStorageService.cs @@ -2,7 +2,7 @@ using System.Diagnostics; using LibHac.Bcat.Detail.Ipc; using LibHac.Bcat.Detail.Service.Core; -using LibHac.Common; +using LibHac.Util; namespace LibHac.Bcat.Detail.Service { diff --git a/src/LibHac/Bcat/Digest.cs b/src/LibHac/Bcat/Digest.cs index cec171c9..a73e26d9 100644 --- a/src/LibHac/Bcat/Digest.cs +++ b/src/LibHac/Bcat/Digest.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Util; namespace LibHac.Bcat { diff --git a/src/LibHac/Bcat/DirectoryName.cs b/src/LibHac/Bcat/DirectoryName.cs index 009f276e..46ded68b 100644 --- a/src/LibHac/Bcat/DirectoryName.cs +++ b/src/LibHac/Bcat/DirectoryName.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Util; namespace LibHac.Bcat { diff --git a/src/LibHac/Bcat/FileName.cs b/src/LibHac/Bcat/FileName.cs index 6911a7eb..7a5b66c5 100644 --- a/src/LibHac/Bcat/FileName.cs +++ b/src/LibHac/Bcat/FileName.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Util; namespace LibHac.Bcat { diff --git a/src/LibHac/Boot/KeyBlob.cs b/src/LibHac/Boot/KeyBlob.cs index 8ad397fe..fc006a2c 100644 --- a/src/LibHac/Boot/KeyBlob.cs +++ b/src/LibHac/Boot/KeyBlob.cs @@ -4,6 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Crypto; +using LibHac.Util; namespace LibHac.Boot { diff --git a/src/LibHac/Boot/Package1.cs b/src/LibHac/Boot/Package1.cs index 7b39fd7b..0e93c426 100644 --- a/src/LibHac/Boot/Package1.cs +++ b/src/LibHac/Boot/Package1.cs @@ -7,6 +7,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common.Keys; using LibHac.Diag; +using LibHac.Util; namespace LibHac.Boot { diff --git a/src/LibHac/Common/Buffer.cs b/src/LibHac/Common/Buffer.cs index 071fdc36..81ff725b 100644 --- a/src/LibHac/Common/Buffer.cs +++ b/src/LibHac/Common/Buffer.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using LibHac.Util; namespace LibHac.Common { diff --git a/src/LibHac/Common/Id128.cs b/src/LibHac/Common/Id128.cs index 9bb2eeec..3473131f 100644 --- a/src/LibHac/Common/Id128.cs +++ b/src/LibHac/Common/Id128.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; +using LibHac.Util; namespace LibHac.Common { diff --git a/src/LibHac/Common/Key128.cs b/src/LibHac/Common/Key128.cs index 10123b8d..dc211a22 100644 --- a/src/LibHac/Common/Key128.cs +++ b/src/LibHac/Common/Key128.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; +using LibHac.Util; namespace LibHac.Common { diff --git a/src/LibHac/Common/Keys/ExternalKeyReader.cs b/src/LibHac/Common/Keys/ExternalKeyReader.cs index dff25718..de44f7a8 100644 --- a/src/LibHac/Common/Keys/ExternalKeyReader.cs +++ b/src/LibHac/Common/Keys/ExternalKeyReader.cs @@ -5,6 +5,7 @@ using System.IO; using System.Runtime.CompilerServices; using LibHac.Fs; using LibHac.Spl; +using LibHac.Util; namespace LibHac.Common.Keys { @@ -148,7 +149,7 @@ namespace LibHac.Common.Keys continue; } - if (!Utilities.TryToBytes(ctx.CurrentValue, key)) + if (!StringUtils.TryFromHexString(ctx.CurrentValue, key)) { key.Clear(); @@ -204,13 +205,13 @@ namespace LibHac.Common.Keys var rightsId = new RightsId(); var titleKey = new AccessKey(); - if (!Utilities.TryToBytes(ctx.CurrentKey, SpanHelpers.AsByteSpan(ref rightsId))) + if (!StringUtils.TryFromHexString(ctx.CurrentKey, SpanHelpers.AsByteSpan(ref rightsId))) { logger?.LogMessage($"Invalid rights ID \"{ctx.CurrentKey.ToString()}\" in title key file"); continue; } - if (!Utilities.TryToBytes(ctx.CurrentValue, SpanHelpers.AsByteSpan(ref titleKey))) + if (!StringUtils.TryFromHexString(ctx.CurrentValue, SpanHelpers.AsByteSpan(ref titleKey))) { logger?.LogMessage($"Invalid title key \"{ctx.CurrentValue.ToString()}\" in title key file"); continue; @@ -261,7 +262,7 @@ namespace LibHac.Common.Keys Delimiter, Value, WhiteSpace2, - End, + End } private static ReaderStatus GetKeyValuePair(ref KvPairReaderContext reader) diff --git a/src/LibHac/Common/Keys/ExternalKeyWriter.cs b/src/LibHac/Common/Keys/ExternalKeyWriter.cs index be3b18ce..3faa290d 100644 --- a/src/LibHac/Common/Keys/ExternalKeyWriter.cs +++ b/src/LibHac/Common/Keys/ExternalKeyWriter.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using LibHac.Fs; using LibHac.Spl; +using LibHac.Util; using Type = LibHac.Common.Keys.KeyInfo.KeyType; using RangeType = LibHac.Common.Keys.KeyInfo.KeyRangeType; diff --git a/src/LibHac/Common/Keys/KeyInfo.cs b/src/LibHac/Common/Keys/KeyInfo.cs index 09d2287f..ba27dc56 100644 --- a/src/LibHac/Common/Keys/KeyInfo.cs +++ b/src/LibHac/Common/Keys/KeyInfo.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using LibHac.Diag; +using LibHac.Util; namespace LibHac.Common.Keys { @@ -140,7 +141,7 @@ namespace LibHac.Common.Keys byte index = default; // Try to get the index of the key name - if (!keyName.Slice(keyName.Length - 2, 2).TryToBytes(SpanHelpers.AsSpan(ref index))) + if (!StringUtils.TryFromHexString(keyName.Slice(keyName.Length - 2, 2), SpanHelpers.AsSpan(ref index))) return false; // Check if the index is in this key's range diff --git a/src/LibHac/Common/PathBuilder.cs b/src/LibHac/Common/PathBuilder.cs index afdd78e7..35998732 100644 --- a/src/LibHac/Common/PathBuilder.cs +++ b/src/LibHac/Common/PathBuilder.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using LibHac.Fs; using LibHac.FsSystem; +using LibHac.Util; namespace LibHac.Common { diff --git a/src/LibHac/Common/U8Span.cs b/src/LibHac/Common/U8Span.cs index 25daf063..c9b68c83 100644 --- a/src/LibHac/Common/U8Span.cs +++ b/src/LibHac/Common/U8Span.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; +using LibHac.Util; namespace LibHac.Common { diff --git a/src/LibHac/Common/U8SpanMutable.cs b/src/LibHac/Common/U8SpanMutable.cs index 7d782ff2..0cd7ea46 100644 --- a/src/LibHac/Common/U8SpanMutable.cs +++ b/src/LibHac/Common/U8SpanMutable.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; +using LibHac.Util; namespace LibHac.Common { diff --git a/src/LibHac/Common/U8String.cs b/src/LibHac/Common/U8String.cs index 56d60eaf..af81643c 100644 --- a/src/LibHac/Common/U8String.cs +++ b/src/LibHac/Common/U8String.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Text; +using LibHac.Util; namespace LibHac.Common { diff --git a/src/LibHac/Common/U8StringBuilder.cs b/src/LibHac/Common/U8StringBuilder.cs index 088e46bc..1b07bc48 100644 --- a/src/LibHac/Common/U8StringBuilder.cs +++ b/src/LibHac/Common/U8StringBuilder.cs @@ -3,6 +3,7 @@ using System.Buffers; using System.Buffers.Text; using System.Diagnostics; using System.Runtime.CompilerServices; +using LibHac.Util; namespace LibHac.Common { diff --git a/src/LibHac/Common/U8StringMutable.cs b/src/LibHac/Common/U8StringMutable.cs index c808014f..b041aeea 100644 --- a/src/LibHac/Common/U8StringMutable.cs +++ b/src/LibHac/Common/U8StringMutable.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Text; +using LibHac.Util; namespace LibHac.Common { diff --git a/src/LibHac/Crypto/KeyTypes.cs b/src/LibHac/Crypto/KeyTypes.cs index ff076f65..17c2d396 100644 --- a/src/LibHac/Crypto/KeyTypes.cs +++ b/src/LibHac/Crypto/KeyTypes.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Util; namespace LibHac.Crypto { diff --git a/src/LibHac/Fs/FileSystemClient.cs b/src/LibHac/Fs/FileSystemClient.cs index 0d42e86f..2de35327 100644 --- a/src/LibHac/Fs/FileSystemClient.cs +++ b/src/LibHac/Fs/FileSystemClient.cs @@ -8,6 +8,7 @@ using LibHac.Fs.Fsa; using LibHac.FsSrv; using LibHac.FsSrv.Sf; using LibHac.FsSystem; +using LibHac.Util; namespace LibHac.Fs { diff --git a/src/LibHac/Fs/InMemoryFileSystem.cs b/src/LibHac/Fs/InMemoryFileSystem.cs index f6513919..7fdb791e 100644 --- a/src/LibHac/Fs/InMemoryFileSystem.cs +++ b/src/LibHac/Fs/InMemoryFileSystem.cs @@ -4,6 +4,7 @@ using System.IO; using LibHac.Common; using LibHac.Fs.Fsa; using LibHac.FsSystem; +using LibHac.Util; namespace LibHac.Fs { diff --git a/src/LibHac/Fs/RightsId.cs b/src/LibHac/Fs/RightsId.cs index 120eec89..ba5216af 100644 --- a/src/LibHac/Fs/RightsId.cs +++ b/src/LibHac/Fs/RightsId.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Util; namespace LibHac.Fs { diff --git a/src/LibHac/Fs/Shim/Bis.cs b/src/LibHac/Fs/Shim/Bis.cs index 44230811..3cae5f21 100644 --- a/src/LibHac/Fs/Shim/Bis.cs +++ b/src/LibHac/Fs/Shim/Bis.cs @@ -4,6 +4,7 @@ using LibHac.Common; using LibHac.Fs.Fsa; using LibHac.FsSrv; using LibHac.FsSystem; +using LibHac.Util; using static LibHac.Fs.CommonMountNames; namespace LibHac.Fs.Shim diff --git a/src/LibHac/Fs/Shim/ContentStorage.cs b/src/LibHac/Fs/Shim/ContentStorage.cs index 0d70994b..7283cc77 100644 --- a/src/LibHac/Fs/Shim/ContentStorage.cs +++ b/src/LibHac/Fs/Shim/ContentStorage.cs @@ -2,6 +2,7 @@ using LibHac.Common; using LibHac.Fs.Fsa; using LibHac.FsSrv; +using LibHac.Util; namespace LibHac.Fs.Shim { diff --git a/src/LibHac/Fs/Shim/Host.cs b/src/LibHac/Fs/Shim/Host.cs index 3b31a8c1..e75080df 100644 --- a/src/LibHac/Fs/Shim/Host.cs +++ b/src/LibHac/Fs/Shim/Host.cs @@ -5,6 +5,7 @@ using LibHac.Common; using LibHac.Fs.Fsa; using LibHac.FsSrv; using LibHac.FsSystem; +using LibHac.Util; using static LibHac.Fs.CommonMountNames; namespace LibHac.Fs.Shim diff --git a/src/LibHac/FsSrv/FileSystemProxy.cs b/src/LibHac/FsSrv/FileSystemProxy.cs index 2c2adb1a..75d266be 100644 --- a/src/LibHac/FsSrv/FileSystemProxy.cs +++ b/src/LibHac/FsSrv/FileSystemProxy.cs @@ -10,6 +10,7 @@ using LibHac.FsSystem; using LibHac.Kvdb; using LibHac.Ncm; using LibHac.Spl; +using LibHac.Util; namespace LibHac.FsSrv { diff --git a/src/LibHac/FsSrv/FileSystemProxyCore.cs b/src/LibHac/FsSrv/FileSystemProxyCore.cs index e677bde6..a216178b 100644 --- a/src/LibHac/FsSrv/FileSystemProxyCore.cs +++ b/src/LibHac/FsSrv/FileSystemProxyCore.cs @@ -9,6 +9,7 @@ using LibHac.FsSystem; using LibHac.FsSrv.Creators; using LibHac.FsSystem.NcaUtils; using LibHac.Spl; +using LibHac.Util; using RightsId = LibHac.Fs.RightsId; namespace LibHac.FsSrv diff --git a/src/LibHac/FsSrv/PathNormalizer.cs b/src/LibHac/FsSrv/PathNormalizer.cs index 43ad4983..8e9f50a9 100644 --- a/src/LibHac/FsSrv/PathNormalizer.cs +++ b/src/LibHac/FsSrv/PathNormalizer.cs @@ -2,6 +2,7 @@ using LibHac.Common; using LibHac.Fs; using LibHac.FsSystem; +using LibHac.Util; namespace LibHac.FsSrv { diff --git a/src/LibHac/FsSrv/Sf/FspPath.cs b/src/LibHac/FsSrv/Sf/FspPath.cs index 49b3e20b..fe314ada 100644 --- a/src/LibHac/FsSrv/Sf/FspPath.cs +++ b/src/LibHac/FsSrv/Sf/FspPath.cs @@ -4,6 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Fs; +using LibHac.Util; namespace LibHac.FsSrv.Sf { diff --git a/src/LibHac/FsSystem/AesXtsDirectory.cs b/src/LibHac/FsSystem/AesXtsDirectory.cs index aa881d48..afd88c3d 100644 --- a/src/LibHac/FsSystem/AesXtsDirectory.cs +++ b/src/LibHac/FsSystem/AesXtsDirectory.cs @@ -2,6 +2,7 @@ using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { @@ -38,7 +39,7 @@ namespace LibHac.FsSystem } else { - string entryName = Utilities.GetUtf8StringNullTerminated(entry.Name); + string entryName = StringUtils.NullTerminatedUtf8ToString(entry.Name); entry.Size = GetAesXtsFileSize(PathTools.Combine(Path.ToString(), entryName).ToU8Span()); } } diff --git a/src/LibHac/FsSystem/ConcatenationDirectory.cs b/src/LibHac/FsSystem/ConcatenationDirectory.cs index 4f4cdd18..e8883b8e 100644 --- a/src/LibHac/FsSystem/ConcatenationDirectory.cs +++ b/src/LibHac/FsSystem/ConcatenationDirectory.cs @@ -3,6 +3,7 @@ using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { @@ -63,7 +64,7 @@ namespace LibHac.FsSystem if (!Mode.HasFlag(OpenDirectoryMode.NoFileSize)) { - string entryName = Utilities.GetUtf8StringNullTerminated(entry.Name); + string entryName = StringUtils.NullTerminatedUtf8ToString(entry.Name); string entryFullPath = PathTools.Combine(_path.ToString(), entryName); rc = ParentFileSystem.GetConcatenationFileSize(out long fileSize, entryFullPath.ToU8Span()); @@ -122,7 +123,7 @@ namespace LibHac.FsSystem } else { - string name = Utilities.GetUtf8StringNullTerminated(entry.Name); + string name = StringUtils.NullTerminatedUtf8ToString(entry.Name); var fullPath = PathTools.Combine(_path.ToString(), name).ToU8Span(); return ParentFileSystem.IsConcatenationFile(fullPath); diff --git a/src/LibHac/FsSystem/ConcatenationFileSystem.cs b/src/LibHac/FsSystem/ConcatenationFileSystem.cs index 5a254faa..e8294e29 100644 --- a/src/LibHac/FsSystem/ConcatenationFileSystem.cs +++ b/src/LibHac/FsSystem/ConcatenationFileSystem.cs @@ -6,6 +6,7 @@ using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { diff --git a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs index 2ee1ddfa..10b5f9e7 100644 --- a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs +++ b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs @@ -2,6 +2,7 @@ using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { diff --git a/src/LibHac/FsSystem/DirectoryUtils.cs b/src/LibHac/FsSystem/DirectoryUtils.cs index 2f26bafe..95ccb02b 100644 --- a/src/LibHac/FsSystem/DirectoryUtils.cs +++ b/src/LibHac/FsSystem/DirectoryUtils.cs @@ -2,6 +2,7 @@ using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { diff --git a/src/LibHac/FsSystem/FileSystemExtensions.cs b/src/LibHac/FsSystem/FileSystemExtensions.cs index 849056b5..33be725c 100644 --- a/src/LibHac/FsSystem/FileSystemExtensions.cs +++ b/src/LibHac/FsSystem/FileSystemExtensions.cs @@ -5,6 +5,7 @@ using System.IO; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { diff --git a/src/LibHac/FsSystem/FsPath.cs b/src/LibHac/FsSystem/FsPath.cs index e11c8c84..ab311210 100644 --- a/src/LibHac/FsSystem/FsPath.cs +++ b/src/LibHac/FsSystem/FsPath.cs @@ -4,6 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Fs; +using LibHac.Util; namespace LibHac.FsSystem { diff --git a/src/LibHac/FsSystem/LayeredFileSystem.cs b/src/LibHac/FsSystem/LayeredFileSystem.cs index ccb5916d..d536ab17 100644 --- a/src/LibHac/FsSystem/LayeredFileSystem.cs +++ b/src/LibHac/FsSystem/LayeredFileSystem.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { diff --git a/src/LibHac/FsSystem/LocalDirectory.cs b/src/LibHac/FsSystem/LocalDirectory.cs index 6d2dd864..9afe2bd3 100644 --- a/src/LibHac/FsSystem/LocalDirectory.cs +++ b/src/LibHac/FsSystem/LocalDirectory.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; -using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { @@ -35,7 +35,7 @@ namespace LibHac.FsSystem if (!CanReturnEntry(isDir, Mode)) continue; - ReadOnlySpan name = Utilities.GetUtf8Bytes(localEntry.Name); + ReadOnlySpan name = StringUtils.StringToUtf8(localEntry.Name); DirectoryEntryType type = isDir ? DirectoryEntryType.Directory : DirectoryEntryType.File; long length = isDir ? 0 : ((FileInfo)localEntry).Length; diff --git a/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs b/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs index 60945f30..9c45b79a 100644 --- a/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs +++ b/src/LibHac/FsSystem/NcaUtils/NcaHeader.cs @@ -7,6 +7,7 @@ using LibHac.Common.Keys; using LibHac.Crypto; using LibHac.Diag; using LibHac.Fs; +using LibHac.Util; namespace LibHac.FsSystem.NcaUtils { diff --git a/src/LibHac/FsSystem/PartitionDirectory.cs b/src/LibHac/FsSystem/PartitionDirectory.cs index 03550613..65522627 100644 --- a/src/LibHac/FsSystem/PartitionDirectory.cs +++ b/src/LibHac/FsSystem/PartitionDirectory.cs @@ -1,9 +1,9 @@ using System; using System.IO; using System.Text; -using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { diff --git a/src/LibHac/FsSystem/PartitionFileSystemCore.cs b/src/LibHac/FsSystem/PartitionFileSystemCore.cs index 01f03440..9ee82613 100644 --- a/src/LibHac/FsSystem/PartitionFileSystemCore.cs +++ b/src/LibHac/FsSystem/PartitionFileSystemCore.cs @@ -5,6 +5,7 @@ using LibHac.Crypto; using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSystem.Detail; +using LibHac.Util; namespace LibHac.FsSystem { diff --git a/src/LibHac/FsSystem/PartitionFileSystemMetaCore.cs b/src/LibHac/FsSystem/PartitionFileSystemMetaCore.cs index a4f6b913..514382a9 100644 --- a/src/LibHac/FsSystem/PartitionFileSystemMetaCore.cs +++ b/src/LibHac/FsSystem/PartitionFileSystemMetaCore.cs @@ -5,6 +5,7 @@ using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Fs; using LibHac.FsSystem.Detail; +using LibHac.Util; namespace LibHac.FsSystem { diff --git a/src/LibHac/FsSystem/PathTools.cs b/src/LibHac/FsSystem/PathTools.cs index 79bbf1ea..d930fea8 100644 --- a/src/LibHac/FsSystem/PathTools.cs +++ b/src/LibHac/FsSystem/PathTools.cs @@ -4,6 +4,7 @@ using System.IO.Enumeration; using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Fs; +using LibHac.Util; namespace LibHac.FsSystem { diff --git a/src/LibHac/FsSystem/RomFs/HierarchicalRomFileTable.cs b/src/LibHac/FsSystem/RomFs/HierarchicalRomFileTable.cs index afefa22e..361ed3ef 100644 --- a/src/LibHac/FsSystem/RomFs/HierarchicalRomFileTable.cs +++ b/src/LibHac/FsSystem/RomFs/HierarchicalRomFileTable.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.InteropServices; using LibHac.Fs; +using LibHac.Util; namespace LibHac.FsSystem.RomFs { @@ -83,7 +84,7 @@ namespace LibHac.FsSystem.RomFs public bool TryOpenFile(string path, out T fileInfo) { - FindPathRecursive(Utilities.GetUtf8Bytes(path), out RomEntryKey key); + FindPathRecursive(StringUtils.StringToUtf8(path), out RomEntryKey key); if (FileTable.TryGetValue(ref key, out RomKeyValuePair keyValuePair)) { @@ -116,7 +117,7 @@ namespace LibHac.FsSystem.RomFs /// otherwise, . public bool TryOpenDirectory(string path, out FindPosition position) { - FindPathRecursive(Utilities.GetUtf8Bytes(path), out RomEntryKey key); + FindPathRecursive(StringUtils.StringToUtf8(path), out RomEntryKey key); if (DirectoryTable.TryGetValue(ref key, out RomKeyValuePair keyValuePair)) { @@ -169,7 +170,7 @@ namespace LibHac.FsSystem.RomFs position.NextFile = entry.NextSibling; info = entry.Info; - name = Utilities.GetUtf8String(nameBytes); + name = StringUtils.Utf8ToString(nameBytes); return true; } @@ -193,7 +194,7 @@ namespace LibHac.FsSystem.RomFs ref DirectoryRomEntry entry = ref DirectoryTable.GetValueReference(position.NextDirectory, out Span nameBytes); position.NextDirectory = entry.NextSibling; - name = Utilities.GetUtf8String(nameBytes); + name = StringUtils.Utf8ToString(nameBytes); return true; } @@ -207,7 +208,7 @@ namespace LibHac.FsSystem.RomFs public void AddFile(string path, ref T fileInfo) { path = PathTools.Normalize(path); - ReadOnlySpan pathBytes = Utilities.GetUtf8Bytes(path); + ReadOnlySpan pathBytes = StringUtils.StringToUtf8(path); if (path == "/") throw new ArgumentException("Path cannot be empty"); @@ -223,7 +224,7 @@ namespace LibHac.FsSystem.RomFs { path = PathTools.Normalize(path); - CreateDirectoryRecursive(Utilities.GetUtf8Bytes(path)); + CreateDirectoryRecursive(StringUtils.StringToUtf8(path)); } /// diff --git a/src/LibHac/FsSystem/RomFs/RomFsDirectory.cs b/src/LibHac/FsSystem/RomFs/RomFsDirectory.cs index 6f2aaae8..635d5047 100644 --- a/src/LibHac/FsSystem/RomFs/RomFsDirectory.cs +++ b/src/LibHac/FsSystem/RomFs/RomFsDirectory.cs @@ -1,8 +1,8 @@ using System; using System.Text; -using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem.RomFs { diff --git a/src/LibHac/FsSystem/Save/HierarchicalSaveFileTable.cs b/src/LibHac/FsSystem/Save/HierarchicalSaveFileTable.cs index 75375bf7..31a4764d 100644 --- a/src/LibHac/FsSystem/Save/HierarchicalSaveFileTable.cs +++ b/src/LibHac/FsSystem/Save/HierarchicalSaveFileTable.cs @@ -3,6 +3,7 @@ using System.IO; using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Fs; +using LibHac.Util; namespace LibHac.FsSystem.Save { @@ -59,7 +60,7 @@ namespace LibHac.FsSystem.Save position.NextFile = entry.NextSibling; info = entry.Value; - name = Utilities.GetUtf8StringNullTerminated(nameBytes); + name = StringUtils.NullTerminatedUtf8ToString(nameBytes); return true; } @@ -85,7 +86,7 @@ namespace LibHac.FsSystem.Save position.NextDirectory = entry.NextSibling; - name = Utilities.GetUtf8StringNullTerminated(nameBytes); + name = StringUtils.NullTerminatedUtf8ToString(nameBytes); return true; } diff --git a/src/LibHac/FsSystem/Save/SaveDataDirectory.cs b/src/LibHac/FsSystem/Save/SaveDataDirectory.cs index 81e6ec6f..074b657e 100644 --- a/src/LibHac/FsSystem/Save/SaveDataDirectory.cs +++ b/src/LibHac/FsSystem/Save/SaveDataDirectory.cs @@ -1,8 +1,8 @@ using System; using System.Text; -using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem.Save { diff --git a/src/LibHac/FsSystem/Save/SaveFsList.cs b/src/LibHac/FsSystem/Save/SaveFsList.cs index add73f18..8af53db0 100644 --- a/src/LibHac/FsSystem/Save/SaveFsList.cs +++ b/src/LibHac/FsSystem/Save/SaveFsList.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using LibHac.Common; using LibHac.Fs; +using LibHac.Util; namespace LibHac.FsSystem.Save { diff --git a/src/LibHac/FsSystem/SubdirectoryFileSystem.cs b/src/LibHac/FsSystem/SubdirectoryFileSystem.cs index 8eb4c861..7d117d92 100644 --- a/src/LibHac/FsSystem/SubdirectoryFileSystem.cs +++ b/src/LibHac/FsSystem/SubdirectoryFileSystem.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { diff --git a/src/LibHac/FsSystem/Utility.cs b/src/LibHac/FsSystem/Utility.cs index a38efa5c..6da63cc5 100644 --- a/src/LibHac/FsSystem/Utility.cs +++ b/src/LibHac/FsSystem/Utility.cs @@ -5,6 +5,7 @@ using LibHac.Common; using LibHac.Diag; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { diff --git a/src/LibHac/Kvdb/BoundedString.cs b/src/LibHac/Kvdb/BoundedString.cs index a94ca2ce..0a687966 100644 --- a/src/LibHac/Kvdb/BoundedString.cs +++ b/src/LibHac/Kvdb/BoundedString.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Util; namespace LibHac.Kvdb { diff --git a/src/LibHac/ResultNameResolver.cs b/src/LibHac/ResultNameResolver.cs index 2a685223..2aee430e 100644 --- a/src/LibHac/ResultNameResolver.cs +++ b/src/LibHac/ResultNameResolver.cs @@ -4,6 +4,7 @@ using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Util; namespace LibHac { diff --git a/src/LibHac/Sm/ServiceName.cs b/src/LibHac/Sm/ServiceName.cs index 78653184..3304d4ff 100644 --- a/src/LibHac/Sm/ServiceName.cs +++ b/src/LibHac/Sm/ServiceName.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using LibHac.Common; +using LibHac.Util; namespace LibHac.Sm { diff --git a/src/LibHac/SwitchFs.cs b/src/LibHac/SwitchFs.cs index ed52dda7..71572bab 100644 --- a/src/LibHac/SwitchFs.cs +++ b/src/LibHac/SwitchFs.cs @@ -12,6 +12,7 @@ using LibHac.FsSystem.NcaUtils; using LibHac.FsSystem.Save; using LibHac.Ncm; using LibHac.Ns; +using LibHac.Util; namespace LibHac { diff --git a/src/LibHac/Util/Impl/HexConverter.cs b/src/LibHac/Util/Impl/HexConverter.cs new file mode 100644 index 00000000..7e0caa0c --- /dev/null +++ b/src/LibHac/Util/Impl/HexConverter.cs @@ -0,0 +1,233 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace LibHac.Util.Impl +{ + internal static class HexConverter + { + public enum Casing : uint + { + // Output [ '0' .. '9' ] and [ 'A' .. 'F' ]. + Upper = 0, + + // Output [ '0' .. '9' ] and [ 'a' .. 'f' ]. + // This works because values in the range [ 0x30 .. 0x39 ] ([ '0' .. '9' ]) + // already have the 0x20 bit set, so ORing them with 0x20 is a no-op, + // while outputs in the range [ 0x41 .. 0x46 ] ([ 'A' .. 'F' ]) + // don't have the 0x20 bit set, so ORing them maps to + // [ 0x61 .. 0x66 ] ([ 'a' .. 'f' ]), which is what we want. + Lower = 0x2020U, + } + + // We want to pack the incoming byte into a single integer [ 0000 HHHH 0000 LLLL ], + // where HHHH and LLLL are the high and low nibbles of the incoming byte. Then + // subtract this integer from a constant minuend as shown below. + // + // [ 1000 1001 1000 1001 ] + // - [ 0000 HHHH 0000 LLLL ] + // ========================= + // [ *YYY **** *ZZZ **** ] + // + // The end result of this is that YYY is 0b000 if HHHH <= 9, and YYY is 0b111 if HHHH >= 10. + // Similarly, ZZZ is 0b000 if LLLL <= 9, and ZZZ is 0b111 if LLLL >= 10. + // (We don't care about the value of asterisked bits.) + // + // To turn a nibble in the range [ 0 .. 9 ] into hex, we calculate hex := nibble + 48 (ascii '0'). + // To turn a nibble in the range [ 10 .. 15 ] into hex, we calculate hex := nibble - 10 + 65 (ascii 'A'). + // => hex := nibble + 55. + // The difference in the starting ASCII offset is (55 - 48) = 7, depending on whether the nibble is <= 9 or >= 10. + // Since 7 is 0b111, this conveniently matches the YYY or ZZZ value computed during the earlier subtraction. + + // The commented out code below is code that directly implements the logic described above. + + // uint packedOriginalValues = (((uint)value & 0xF0U) << 4) + ((uint)value & 0x0FU); + // uint difference = 0x8989U - packedOriginalValues; + // uint add7Mask = (difference & 0x7070U) >> 4; // line YYY and ZZZ back up with the packed values + // uint packedResult = packedOriginalValues + add7Mask + 0x3030U /* ascii '0' */; + + // The code below is equivalent to the commented out code above but has been tweaked + // to allow codegen to make some extra optimizations. + + // The low byte of the packed result contains the hex representation of the incoming byte's low nibble. + // The adjacent byte of the packed result contains the hex representation of the incoming byte's high nibble. + + // Finally, write to the output buffer starting with the *highest* index so that codegen can + // elide all but the first bounds check. (This only works if 'startingIndex' is a compile-time constant.) + + // The JIT can elide bounds checks if 'startingIndex' is constant and if the caller is + // writing to a span of known length (or the caller has already checked the bounds of the + // furthest access). + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ToBytesBuffer(byte value, Span buffer, int startingIndex = 0, Casing casing = Casing.Upper) + { + uint difference = ((value & 0xF0U) << 4) + (value & 0x0FU) - 0x8989U; + uint packedResult = ((((uint)(-(int)difference) & 0x7070U) >> 4) + difference + 0xB9B9U) | (uint)casing; + + buffer[startingIndex + 1] = (byte)packedResult; + buffer[startingIndex] = (byte)(packedResult >> 8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ToCharsBuffer(byte value, Span buffer, int startingIndex = 0, Casing casing = Casing.Upper) + { + uint difference = ((value & 0xF0U) << 4) + (value & 0x0FU) - 0x8989U; + uint packedResult = ((((uint)(-(int)difference) & 0x7070U) >> 4) + difference + 0xB9B9U) | (uint)casing; + + buffer[startingIndex + 1] = (char)(packedResult & 0xFF); + buffer[startingIndex] = (char)(packedResult >> 8); + } + + public static void EncodeToUtf16(ReadOnlySpan bytes, Span chars, Casing casing = Casing.Upper) + { + Debug.Assert(chars.Length >= bytes.Length * 2); + + for (int pos = 0; pos < bytes.Length; ++pos) + { + ToCharsBuffer(bytes[pos], chars, pos * 2, casing); + } + } + + public static unsafe string ToString(ReadOnlySpan bytes, Casing casing = Casing.Upper) + { + fixed (byte* bytesPtr = bytes) + { + // Todo: Make lambda static in C# 9 + return string.Create(bytes.Length * 2, (Ptr: (IntPtr)bytesPtr, bytes.Length, casing), (chars, args) => + { + var ros = new ReadOnlySpan((byte*)args.Ptr, args.Length); + EncodeToUtf16(ros, chars, args.casing); + }); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static char ToCharUpper(int value) + { + value &= 0xF; + value += '0'; + + if (value > '9') + { + value += ('A' - ('9' + 1)); + } + + return (char)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static char ToCharLower(int value) + { + value &= 0xF; + value += '0'; + + if (value > '9') + { + value += ('a' - ('9' + 1)); + } + + return (char)value; + } + + public static bool TryDecodeFromUtf16(ReadOnlySpan chars, Span bytes) + { + return TryDecodeFromUtf16(chars, bytes, out _); + } + + public static bool TryDecodeFromUtf16(ReadOnlySpan chars, Span bytes, out int charsProcessed) + { + Debug.Assert(chars.Length % 2 == 0, "Un-even number of characters provided"); + Debug.Assert(chars.Length / 2 == bytes.Length, "Target buffer not right-sized for provided characters"); + + int i = 0; + int j = 0; + int byteLo = 0; + int byteHi = 0; + while (j < bytes.Length) + { + byteLo = FromChar(chars[i + 1]); + byteHi = FromChar(chars[i]); + + // byteHi hasn't been shifted to the high half yet, so the only way the bitwise or produces this pattern + // is if either byteHi or byteLo was not a hex character. + if ((byteLo | byteHi) == 0xFF) + break; + + bytes[j++] = (byte)((byteHi << 4) | byteLo); + i += 2; + } + + if (byteLo == 0xFF) + i++; + + charsProcessed = i; + return (byteLo | byteHi) != 0xFF; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int FromChar(int c) + { + return c >= CharToHexLookup.Length ? 0xFF : CharToHexLookup[c]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int FromUpperChar(int c) + { + return c > 71 ? 0xFF : CharToHexLookup[c]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int FromLowerChar(int c) + { + if ((uint)(c - '0') <= '9' - '0') + return c - '0'; + + if ((uint)(c - 'a') <= 'f' - 'a') + return c - 'a' + 10; + + return 0xFF; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsHexChar(int c) + { + return FromChar(c) != 0xFF; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsHexUpperChar(int c) + { + return (uint)(c - '0') <= 9 || (uint)(c - 'A') <= ('F' - 'A'); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsHexLowerChar(int c) + { + return (uint)(c - '0') <= 9 || (uint)(c - 'a') <= ('f' - 'a'); + } + + /// Map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit. + public static ReadOnlySpan CharToHexLookup => new byte[] + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47 + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 63 + 0xFF, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95 + 0xFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 111 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 127 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 143 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 159 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 175 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 191 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 207 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 223 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 239 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // 255 + }; + } +} \ No newline at end of file diff --git a/src/LibHac/Common/StringUtils.cs b/src/LibHac/Util/StringUtils.cs similarity index 66% rename from src/LibHac/Common/StringUtils.cs rename to src/LibHac/Util/StringUtils.cs index 4832e46f..b20e2b62 100644 --- a/src/LibHac/Common/StringUtils.cs +++ b/src/LibHac/Util/StringUtils.cs @@ -1,7 +1,8 @@ using System; using System.Text; +using LibHac.Util.Impl; -namespace LibHac.Common +namespace LibHac.Util { public static class StringUtils { @@ -159,11 +160,23 @@ namespace LibHac.Common return iDest; } + public static ReadOnlySpan StringToUtf8(string value) + { + return Encoding.UTF8.GetBytes(value).AsSpan(); + } + public static string Utf8ToString(ReadOnlySpan value) { return Encoding.UTF8.GetString(value); } + public static string NullTerminatedUtf8ToString(ReadOnlySpan value) + { + int length = GetLength(value); + + return Encoding.UTF8.GetString(value.Slice(0, length)); + } + public static string Utf8ZToString(ReadOnlySpan value) { return Utf8ToString(value.Slice(0, GetLength(value))); @@ -184,5 +197,69 @@ namespace LibHac.Common return (uint)(c - (byte)'0') <= 9 || (c | 0x20u) - (byte)'a' <= 'f' - 'a'; } + + public static bool TryFromHexString(ReadOnlySpan chars, Span outputBytes) + { + if ((uint)chars.Length % 2 != 0) + return false; + + uint bytesLength = (uint)chars.Length / 2; + + if ((uint)outputBytes.Length >= bytesLength) + { + Span bytes = outputBytes.Slice(0, (int)bytesLength); + return HexConverter.TryDecodeFromUtf16(chars, bytes); + } + + return false; + } + + public static byte[] ToBytes(this string input) + { + return FromHexString(input); + } + + public static byte[] FromHexString(string input) + { + if ((uint)input.Length % 2 != 0) + throw new FormatException("Hex input must be a multiple of 2."); + + var result = new byte[input.Length >> 1]; + + if (!HexConverter.TryDecodeFromUtf16(input, result)) + { + throw new FormatException("Hex input contains invalid characters."); + } + + return result; + } + + public static void FromHexString(ReadOnlySpan chars, Span outputBytes) + { + if ((uint)chars.Length % 2 != 0) + throw new FormatException("Hex input must be a multiple of 2."); + + uint bytesLength = (uint)chars.Length / 2; + + if ((uint)outputBytes.Length >= bytesLength) + { + Span bytes = outputBytes.Slice(0, (int)bytesLength); + + if (!HexConverter.TryDecodeFromUtf16(chars, bytes)) + { + throw new FormatException("Hex input contains invalid characters."); + } + } + + throw new ArgumentException("Buffer is not large enough to fit the input hex string.", nameof(outputBytes)); + } + + public static string ToHexString(this byte[] bytes) => ToHexString((ReadOnlySpan)bytes); + public static string ToHexString(this Span bytes) => ToHexString((ReadOnlySpan)bytes); + + public static string ToHexString(this ReadOnlySpan bytes) + { + return HexConverter.ToString(bytes); + } } } diff --git a/src/LibHac/Utilities.cs b/src/LibHac/Utilities.cs index 1b6a5a78..ad8f0061 100644 --- a/src/LibHac/Utilities.cs +++ b/src/LibHac/Utilities.cs @@ -67,27 +67,6 @@ namespace LibHac return a1.SequenceEqual(a2); } - public static ReadOnlySpan GetUtf8Bytes(string value) - { - return Encoding.UTF8.GetBytes(value).AsSpan(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string GetUtf8String(ReadOnlySpan value) - { - return Encoding.UTF8.GetString(value); - } - - public static string GetUtf8StringNullTerminated(ReadOnlySpan value) - { - int i; - for (i = 0; i < value.Length && value[i] != 0; i++) { } - - value = value.Slice(0, i); - - return Encoding.UTF8.GetString(value); - } - public static bool IsEmpty(this byte[] array) => ((ReadOnlySpan)array).IsEmpty(); public static bool IsEmpty(this Span span) => ((ReadOnlySpan)span).IsEmpty(); @@ -234,147 +213,6 @@ namespace LibHac return Encoding.UTF8.GetString(reader.ReadBytes(size), 0, size); } - private static bool TryHexToInt(char c, out int value) - { - switch (c) - { - case '0': - value = 0; break; - case '1': - value = 1; break; - case '2': - value = 2; break; - case '3': - value = 3; break; - case '4': - value = 4; break; - case '5': - value = 5; break; - case '6': - value = 6; break; - case '7': - value = 7; break; - case '8': - value = 8; break; - case '9': - value = 9; break; - case 'a': - case 'A': - value = 10; break; - case 'b': - case 'B': - value = 11; break; - case 'c': - case 'C': - value = 12; break; - case 'd': - case 'D': - value = 13; break; - case 'e': - case 'E': - value = 14; break; - case 'f': - case 'F': - value = 15; break; - default: - value = 0; - return false; - } - - return true; - } - - private static readonly byte[,] ByteLookup = { - {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, - {0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0} - }; - - public static byte[] ToBytes(this string input) - { - var result = new byte[(input.Length + 1) >> 1]; - int lastcell = result.Length - 1; - int lastchar = input.Length - 1; - for (int i = 0; i < input.Length; i++) - { - if (!TryHexToInt(input[lastchar - i], out int hexInt)) - { - throw new FormatException($"Unrecognized hex char {input[lastchar - i]}"); - } - - result[lastcell - (i >> 1)] |= ByteLookup[i & 1, hexInt]; - } - return result; - } - - public static bool TryToBytes(this string input, out byte[] bytes) - { - var result = new byte[(input.Length + 1) >> 1]; - int lastcell = result.Length - 1; - int lastchar = input.Length - 1; - for (int i = 0; i < input.Length; i++) - { - if (!TryHexToInt(input[lastchar - i], out int hexInt)) - { - bytes = null; - return false; - } - - result[lastcell - (i >> 1)] |= ByteLookup[i & 1, hexInt]; - } - bytes = result; - return true; - } - - public static bool TryToBytes(this ReadOnlySpan input, Span output) - { - if (input.Length != output.Length * 2) - return false; - - int lastcell = output.Length - 1; - int lastchar = input.Length - 1; - for (int i = 0; i < input.Length; i++) - { - if (!TryHexToInt(input[lastchar - i], out int hexInt)) - { - return false; - } - - output[lastcell - (i >> 1)] |= ByteLookup[i & 1, hexInt]; - } - - return true; - } - - private static readonly uint[] Lookup32 = CreateLookup32(); - - private static uint[] CreateLookup32() - { - var result = new uint[256]; - for (int i = 0; i < 256; i++) - { - string s = i.ToString("X2"); - result[i] = s[0] + ((uint)s[1] << 16); - } - return result; - } - - public static string ToHexString(this byte[] bytes) => ToHexString(bytes.AsSpan()); - - public static string ToHexString(this Span bytes) => ToHexString((ReadOnlySpan)bytes); - - public static string ToHexString(this ReadOnlySpan bytes) - { - uint[] lookup32 = Lookup32; - var result = new char[bytes.Length * 2]; - for (int i = 0; i < bytes.Length; i++) - { - uint val = lookup32[bytes[i]]; - result[2 * i] = (char)val; - result[2 * i + 1] = (char)(val >> 16); - } - return new string(result); - } - public static long MediaToReal(long media) { return MediaSize * media; diff --git a/src/hactoolnet/ProcessNax0.cs b/src/hactoolnet/ProcessNax0.cs index 5507fe85..22c2cb52 100644 --- a/src/hactoolnet/ProcessNax0.cs +++ b/src/hactoolnet/ProcessNax0.cs @@ -6,6 +6,7 @@ using LibHac.Common; using LibHac.Crypto; using LibHac.Fs; using LibHac.FsSystem; +using LibHac.Util; using static hactoolnet.Print; namespace hactoolnet @@ -67,7 +68,7 @@ namespace hactoolnet AesXtsFileHeader header = xtsFile.Header; uint magic = header.Magic; - PrintItem(sb, colLen, " Magic:", Utilities.GetUtf8String(SpanHelpers.AsReadOnlyByteSpan(in magic))); + PrintItem(sb, colLen, " Magic:", StringUtils.Utf8ToString(SpanHelpers.AsReadOnlyByteSpan(in magic))); PrintItem(sb, colLen, " Content Type:", GetContentType(contentType)); PrintItem(sb, colLen, " Content Size:", $"{header.Size:x12}"); PrintItem(sb, colLen, " Header HMAC:", header.Signature); diff --git a/src/hactoolnet/ProcessPfs.cs b/src/hactoolnet/ProcessPfs.cs index b8334432..f2f474ad 100644 --- a/src/hactoolnet/ProcessPfs.cs +++ b/src/hactoolnet/ProcessPfs.cs @@ -4,6 +4,7 @@ using System.Text; using LibHac; using LibHac.Fs; using LibHac.FsSystem; +using LibHac.Util; using static hactoolnet.Print; namespace hactoolnet diff --git a/src/hactoolnet/Program.cs b/src/hactoolnet/Program.cs index 352231f6..2b0dfe6c 100644 --- a/src/hactoolnet/Program.cs +++ b/src/hactoolnet/Program.cs @@ -4,6 +4,7 @@ using System.Text; using LibHac; using LibHac.Common.Keys; using LibHac.Fs; +using LibHac.Util; namespace hactoolnet { diff --git a/tests/LibHac.Tests/AesCmac.cs b/tests/LibHac.Tests/AesCmac.cs index 52e2dd2d..a04c13e5 100644 --- a/tests/LibHac.Tests/AesCmac.cs +++ b/tests/LibHac.Tests/AesCmac.cs @@ -1,5 +1,6 @@ using System; using LibHac.Crypto; +using LibHac.Util; using Xunit; namespace LibHac.Tests diff --git a/tests/LibHac.Tests/AesXts.cs b/tests/LibHac.Tests/AesXts.cs index df7a9629..ef36380d 100644 --- a/tests/LibHac.Tests/AesXts.cs +++ b/tests/LibHac.Tests/AesXts.cs @@ -1,5 +1,6 @@ using System.Linq; using LibHac.FsSystem; +using LibHac.Util; using Xunit; namespace LibHac.Tests diff --git a/tests/LibHac.Tests/CryptoTests/RspReader.cs b/tests/LibHac.Tests/CryptoTests/RspReader.cs index 47601eda..7ec0819d 100644 --- a/tests/LibHac.Tests/CryptoTests/RspReader.cs +++ b/tests/LibHac.Tests/CryptoTests/RspReader.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using LibHac.Util; using Xunit; namespace LibHac.Tests.CryptoTests diff --git a/tests/LibHac.Tests/Fs/IFileSystemTestBase/IFileSystemTests.IDirectory.cs b/tests/LibHac.Tests/Fs/IFileSystemTestBase/IFileSystemTests.IDirectory.cs index e5463fb2..0d3c9bc5 100644 --- a/tests/LibHac.Tests/Fs/IFileSystemTestBase/IFileSystemTests.IDirectory.cs +++ b/tests/LibHac.Tests/Fs/IFileSystemTestBase/IFileSystemTests.IDirectory.cs @@ -2,6 +2,7 @@ using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; using Xunit; namespace LibHac.Tests.Fs.IFileSystemTestBase diff --git a/tests/LibHac.Tests/Fs/LayeredFileSystemTests.cs b/tests/LibHac.Tests/Fs/LayeredFileSystemTests.cs index 0c9e3d22..7544595b 100644 --- a/tests/LibHac.Tests/Fs/LayeredFileSystemTests.cs +++ b/tests/LibHac.Tests/Fs/LayeredFileSystemTests.cs @@ -3,6 +3,7 @@ using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSystem; +using LibHac.Util; using Xunit; namespace LibHac.Tests.Fs diff --git a/tests/LibHac.Tests/Fs/PathToolTests.cs b/tests/LibHac.Tests/Fs/PathToolTests.cs index 24818b11..1b5b0b8e 100644 --- a/tests/LibHac.Tests/Fs/PathToolTests.cs +++ b/tests/LibHac.Tests/Fs/PathToolTests.cs @@ -1,5 +1,6 @@ using LibHac.Common; using LibHac.Fs; +using LibHac.Util; using Xunit; namespace LibHac.Tests.Fs diff --git a/tests/LibHac.Tests/PathToolsTests.cs b/tests/LibHac.Tests/PathToolsTests.cs index 16d59c18..d6284594 100644 --- a/tests/LibHac.Tests/PathToolsTests.cs +++ b/tests/LibHac.Tests/PathToolsTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using LibHac.Common; using LibHac.FsSystem; +using LibHac.Util; using Xunit; namespace LibHac.Tests