diff --git a/src/LibHac/Boot/Package1.cs b/src/LibHac/Boot/Package1.cs index 971a8a38..962e944d 100644 --- a/src/LibHac/Boot/Package1.cs +++ b/src/LibHac/Boot/Package1.cs @@ -340,7 +340,7 @@ namespace LibHac.Boot int pk11Size = Unsafe.SizeOf() + GetSectionSize(Package1Section.WarmBoot) + GetSectionSize(Package1Section.Bootloader) + GetSectionSize(Package1Section.SecureMonitor); - pk11Size = Utilities.AlignUp(pk11Size, 0x10); + pk11Size = Alignment.AlignUp(pk11Size, 0x10); return pk11Size == Pk11Size; } diff --git a/src/LibHac/Crypto/Detail/AesCtrMode.cs b/src/LibHac/Crypto/Detail/AesCtrMode.cs index e22ca946..80470619 100644 --- a/src/LibHac/Crypto/Detail/AesCtrMode.cs +++ b/src/LibHac/Crypto/Detail/AesCtrMode.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Cryptography; using LibHac.Common; +using LibHac.Util; namespace LibHac.Crypto.Detail { @@ -25,7 +26,7 @@ namespace LibHac.Crypto.Detail public void Transform(ReadOnlySpan input, Span output) { - int blockCount = Utilities.DivideByRoundUp(input.Length, Aes.BlockSize); + int blockCount = BitUtil.DivideUp(input.Length, Aes.BlockSize); int length = blockCount * Aes.BlockSize; using var counterBuffer = new RentedArray(length); diff --git a/src/LibHac/Crypto/Rsa.cs b/src/LibHac/Crypto/Rsa.cs index ead858b2..b2758548 100644 --- a/src/LibHac/Crypto/Rsa.cs +++ b/src/LibHac/Crypto/Rsa.cs @@ -59,7 +59,7 @@ namespace LibHac.Crypto BigInteger dp = d % (p - BigInteger.One); BigInteger dq = d % (q - BigInteger.One); - BigInteger inverseQ = Utilities.ModInverse(q, p); + BigInteger inverseQ = ModInverse(q, p); byte[] nBytes = n.ToByteArray(); int modLen = nBytes.Length; @@ -138,7 +138,7 @@ namespace LibHac.Crypto do { rng.NextBytes(rndBuf); - g = Utilities.GetBigInteger(rndBuf); + g = GetBigInteger(rndBuf); } while (g >= n); @@ -179,5 +179,66 @@ namespace LibHac.Crypto return (p, q); } + + private static BigInteger GetBigInteger(this ReadOnlySpan bytes) + { + var signPadded = new byte[bytes.Length + 1]; + bytes.CopyTo(signPadded.AsSpan(1)); + Array.Reverse(signPadded); + return new BigInteger(signPadded); + } + + private static byte[] GetBytes(this BigInteger value, int size) + { + byte[] bytes = value.ToByteArray(); + + if (size == -1) + { + size = bytes.Length; + } + + if (bytes.Length > size + 1) + { + throw new InvalidOperationException($"Cannot squeeze value {value} to {size} bytes from {bytes.Length}."); + } + + if (bytes.Length == size + 1 && bytes[bytes.Length - 1] != 0) + { + throw new InvalidOperationException($"Cannot squeeze value {value} to {size} bytes from {bytes.Length}."); + } + + Array.Resize(ref bytes, size); + Array.Reverse(bytes); + return bytes; + } + + private static BigInteger ModInverse(BigInteger e, BigInteger n) + { + BigInteger r = n; + BigInteger newR = e; + BigInteger t = 0; + BigInteger newT = 1; + + while (newR != 0) + { + BigInteger quotient = r / newR; + BigInteger temp; + + temp = t; + t = newT; + newT = temp - quotient * newT; + + temp = r; + r = newR; + newR = temp - quotient * newR; + } + + if (t < 0) + { + t = t + n; + } + + return t; + } } } diff --git a/src/LibHac/Fs/ApplicationSaveDataManagement.cs b/src/LibHac/Fs/ApplicationSaveDataManagement.cs index d1b44184..18cb612f 100644 --- a/src/LibHac/Fs/ApplicationSaveDataManagement.cs +++ b/src/LibHac/Fs/ApplicationSaveDataManagement.cs @@ -4,6 +4,7 @@ using LibHac.Account; using LibHac.Fs.Shim; using LibHac.Ncm; using LibHac.Ns; +using LibHac.Util; namespace LibHac.Fs { @@ -101,7 +102,7 @@ namespace LibHac.Fs if (queryRc.IsFailure()) return queryRc; - requiredSizeSum += Utilities.AlignUp(tempSaveTotalSize, 0x4000) + 0x4000; + requiredSizeSum += Alignment.AlignUp(tempSaveTotalSize, 0x4000) + 0x4000; } } else @@ -118,7 +119,7 @@ namespace LibHac.Fs if (queryRc.IsFailure()) return queryRc; - requiredSizeSum += Utilities.AlignUp(tempSaveTotalSize, 0x4000) + 0x4000; + requiredSizeSum += Alignment.AlignUp(tempSaveTotalSize, 0x4000) + 0x4000; } else if (ResultFs.PathAlreadyExists.Includes(createRc)) { @@ -162,7 +163,7 @@ namespace LibHac.Fs Result queryRc = fs.QuerySaveDataTotalSize(out long totalSize, dataSize, journalSize); if (queryRc.IsFailure()) return queryRc; - requiredSize += Utilities.AlignUp(totalSize, 0x4000) + baseSize; + requiredSize += Alignment.AlignUp(totalSize, 0x4000) + baseSize; } else if (!ResultFs.PathAlreadyExists.Includes(rc)) { diff --git a/src/LibHac/FsSrv/Impl/AccessControl.cs b/src/LibHac/FsSrv/Impl/AccessControl.cs index 1122a49d..b3714de1 100644 --- a/src/LibHac/FsSrv/Impl/AccessControl.cs +++ b/src/LibHac/FsSrv/Impl/AccessControl.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Diag; +using LibHac.Util; namespace LibHac.FsSrv.Impl { @@ -127,7 +128,7 @@ namespace LibHac.FsSrv.Impl accessControlData.Slice(data.SaveDataOwnerInfoOffset + sizeof(int), infoCount); // The ID list must be 4-byte aligned - int idsOffset = Utilities.AlignUp(data.SaveDataOwnerInfoOffset + sizeof(int) + infoCount, 4); + int idsOffset = Alignment.AlignUp(data.SaveDataOwnerInfoOffset + sizeof(int) + infoCount, 4); ReadOnlySpan ids = MemoryMarshal.Cast( accessControlData.Slice(idsOffset, infoCount * sizeof(ulong))); diff --git a/src/LibHac/FsSystem/Aes128CtrTransform.cs b/src/LibHac/FsSystem/Aes128CtrTransform.cs index 3d2ddaef..cbc01883 100644 --- a/src/LibHac/FsSystem/Aes128CtrTransform.cs +++ b/src/LibHac/FsSystem/Aes128CtrTransform.cs @@ -3,6 +3,7 @@ using System.Buffers; using System.Buffers.Binary; using System.Runtime.InteropServices; using System.Security.Cryptography; +using LibHac.Util; namespace LibHac.FsSystem { @@ -36,7 +37,7 @@ namespace LibHac.FsSystem public int TransformBlock(Span data) { - int blockCount = Utilities.DivideByRoundUp(data.Length, BlockSizeBytes); + int blockCount = BitUtil.DivideUp(data.Length, BlockSizeBytes); int length = blockCount * BlockSizeBytes; byte[] counterXor = ArrayPool.Shared.Rent(length); diff --git a/src/LibHac/FsSystem/Aes128XtsTransform.cs b/src/LibHac/FsSystem/Aes128XtsTransform.cs index 90fc19ab..51ab087c 100644 --- a/src/LibHac/FsSystem/Aes128XtsTransform.cs +++ b/src/LibHac/FsSystem/Aes128XtsTransform.cs @@ -28,6 +28,7 @@ using System; using System.Buffers; using System.Runtime.InteropServices; using System.Security.Cryptography; +using LibHac.Util; namespace LibHac.FsSystem { @@ -83,7 +84,7 @@ namespace LibHac.FsSystem /* get number of blocks */ int m = count >> 4; int mo = count & 15; - int alignedCount = Utilities.AlignUp(count, BlockSizeBytes); + int alignedCount = Alignment.AlignUp(count, BlockSizeBytes); /* for i = 0 to m-2 do */ if (mo == 0) diff --git a/src/LibHac/FsSystem/AesXtsFile.cs b/src/LibHac/FsSystem/AesXtsFile.cs index 1f08138a..c4514c37 100644 --- a/src/LibHac/FsSystem/AesXtsFile.cs +++ b/src/LibHac/FsSystem/AesXtsFile.cs @@ -2,6 +2,7 @@ using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { @@ -37,7 +38,7 @@ namespace LibHac.FsSystem ThrowHelper.ThrowResult(ResultFs.AesXtsFileHeaderInvalidKeys.Value, "NAX0 key derivation failed."); } - if (HeaderLength + Utilities.AlignUp(Header.Size, 0x10) > fileSize) + if (HeaderLength + Alignment.AlignUp(Header.Size, 0x10) > fileSize) { ThrowHelper.ThrowResult(ResultFs.AesXtsFileTooShort.Value, "NAX0 key derivation failed."); } @@ -118,7 +119,7 @@ namespace LibHac.FsSystem Result rc = BaseFile.Write(0, Header.ToBytes(false)); if (rc.IsFailure()) return rc; - return BaseStorage.SetSize(Utilities.AlignUp(size, 0x10)); + return BaseStorage.SetSize(Alignment.AlignUp(size, 0x10)); } protected override void Dispose(bool disposing) diff --git a/src/LibHac/FsSystem/AesXtsFileSystem.cs b/src/LibHac/FsSystem/AesXtsFileSystem.cs index 41cc8320..1cf16fc2 100644 --- a/src/LibHac/FsSystem/AesXtsFileSystem.cs +++ b/src/LibHac/FsSystem/AesXtsFileSystem.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { @@ -62,7 +63,7 @@ namespace LibHac.FsSystem /// The 256-bit key containing a 128-bit data key followed by a 128-bit tweak key. public Result CreateFile(U8Span path, long size, CreateFileOptions options, byte[] key) { - long containerSize = AesXtsFile.HeaderLength + Utilities.AlignUp(size, 0x10); + long containerSize = AesXtsFile.HeaderLength + Alignment.AlignUp(size, 0x10); Result rc = BaseFileSystem.CreateFile(path, containerSize, options); if (rc.IsFailure()) return rc; diff --git a/src/LibHac/FsSystem/BucketTree.cs b/src/LibHac/FsSystem/BucketTree.cs index a6ae4ab1..b5ac327f 100644 --- a/src/LibHac/FsSystem/BucketTree.cs +++ b/src/LibHac/FsSystem/BucketTree.cs @@ -6,6 +6,7 @@ using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Diag; using LibHac.Fs; +using LibHac.Util; namespace LibHac.FsSystem { @@ -37,7 +38,7 @@ namespace LibHac.FsSystem Assert.AssertTrue(entrySize >= sizeof(long)); Assert.AssertTrue(nodeSize >= entrySize + Unsafe.SizeOf()); Assert.AssertTrue(NodeSizeMin <= nodeSize && nodeSize <= NodeSizeMax); - Assert.AssertTrue(Utilities.IsPowerOfTwo(nodeSize)); + Assert.AssertTrue(BitUtil.IsPowerOfTwo(nodeSize)); Assert.AssertTrue(!IsInitialized()); // Ensure valid entry count. @@ -139,7 +140,7 @@ namespace LibHac.FsSystem Assert.AssertTrue(entrySize >= sizeof(long)); Assert.AssertTrue(nodeSize >= entrySize + Unsafe.SizeOf()); Assert.AssertTrue(NodeSizeMin <= nodeSize && nodeSize <= NodeSizeMax); - Assert.AssertTrue(Utilities.IsPowerOfTwo(nodeSize)); + Assert.AssertTrue(BitUtil.IsPowerOfTwo(nodeSize)); Assert.AssertTrue(entryCount >= 0); if (entryCount <= 0) @@ -153,7 +154,7 @@ namespace LibHac.FsSystem Assert.AssertTrue(entrySize >= sizeof(long)); Assert.AssertTrue(nodeSize >= entrySize + Unsafe.SizeOf()); Assert.AssertTrue(NodeSizeMin <= nodeSize && nodeSize <= NodeSizeMax); - Assert.AssertTrue(Utilities.IsPowerOfTwo(nodeSize)); + Assert.AssertTrue(BitUtil.IsPowerOfTwo(nodeSize)); Assert.AssertTrue(entryCount >= 0); if (entryCount <= 0) @@ -175,7 +176,7 @@ namespace LibHac.FsSystem private static int GetEntrySetCount(long nodeSize, long entrySize, int entryCount) { int entryCountPerNode = GetEntryCount(nodeSize, entrySize); - return Utilities.DivideByRoundUp(entryCount, entryCountPerNode); + return BitUtil.DivideUp(entryCount, entryCountPerNode); } public static int GetNodeL2Count(long nodeSize, long entrySize, int entryCount) @@ -186,10 +187,10 @@ namespace LibHac.FsSystem if (entrySetCount <= offsetCountPerNode) return 0; - int nodeL2Count = Utilities.DivideByRoundUp(entrySetCount, offsetCountPerNode); + int nodeL2Count = BitUtil.DivideUp(entrySetCount, offsetCountPerNode); Abort.DoAbortUnless(nodeL2Count <= offsetCountPerNode); - return Utilities.DivideByRoundUp(entrySetCount - (offsetCountPerNode - (nodeL2Count - 1)), offsetCountPerNode); + return BitUtil.DivideUp(entrySetCount - (offsetCountPerNode - (nodeL2Count - 1)), offsetCountPerNode); } private static long GetBucketTreeEntryOffset(long entrySetOffset, long entrySize, int entryIndex) diff --git a/src/LibHac/FsSystem/BucketTreeBuilder.cs b/src/LibHac/FsSystem/BucketTreeBuilder.cs index 68dc975c..1d3d2aac 100644 --- a/src/LibHac/FsSystem/BucketTreeBuilder.cs +++ b/src/LibHac/FsSystem/BucketTreeBuilder.cs @@ -4,6 +4,7 @@ using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Diag; using LibHac.Fs; +using LibHac.Util; namespace LibHac.FsSystem { @@ -44,7 +45,7 @@ namespace LibHac.FsSystem Assert.AssertTrue(entrySize >= sizeof(long)); Assert.AssertTrue(nodeSize >= entrySize + Unsafe.SizeOf()); Assert.AssertTrue(NodeSizeMin <= nodeSize && nodeSize <= NodeSizeMax); - Assert.AssertTrue(Utilities.IsPowerOfTwo(nodeSize)); + Assert.AssertTrue(BitUtil.IsPowerOfTwo(nodeSize)); if (headerStorage is null || nodeStorage is null || entryStorage is null) return ResultFs.NullptrArgument.Log(); @@ -267,7 +268,7 @@ namespace LibHac.FsSystem if (rc.IsFailure()) return rc; } - int l2NodeIndex = Utilities.DivideByRoundUp(CurrentL2OffsetIndex, OffsetsPerNode) - 2; + int l2NodeIndex = BitUtil.DivideUp(CurrentL2OffsetIndex, OffsetsPerNode) - 2; int indexInL2Node = CurrentL2OffsetIndex % OffsetsPerNode; // Finalize the current L2 node if needed @@ -291,7 +292,7 @@ namespace LibHac.FsSystem // L1 count depends on the existence or absence of L2 nodes if (CurrentL2OffsetIndex == 0) { - l1NodeHeader.Count = Utilities.DivideByRoundUp(CurrentEntryIndex, EntriesPerEntrySet); + l1NodeHeader.Count = BitUtil.DivideUp(CurrentEntryIndex, EntriesPerEntrySet); } else { diff --git a/src/LibHac/FsSystem/ConcatenationFile.cs b/src/LibHac/FsSystem/ConcatenationFile.cs index 2b081ec9..6fbfb658 100644 --- a/src/LibHac/FsSystem/ConcatenationFile.cs +++ b/src/LibHac/FsSystem/ConcatenationFile.cs @@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { @@ -227,7 +228,7 @@ namespace LibHac.FsSystem if (size == 0) return 1; - return (int)Utilities.DivideByRoundUp(size, subFileSize); + return (int)BitUtil.DivideUp(size, subFileSize); } private static long QuerySubFileSize(int subFileIndex, long totalSize, long subFileSize) diff --git a/src/LibHac/FsSystem/HierarchicalIntegrityVerificationStorage.cs b/src/LibHac/FsSystem/HierarchicalIntegrityVerificationStorage.cs index 4864e886..2df603f0 100644 --- a/src/LibHac/FsSystem/HierarchicalIntegrityVerificationStorage.cs +++ b/src/LibHac/FsSystem/HierarchicalIntegrityVerificationStorage.cs @@ -4,6 +4,7 @@ using System.IO; using System.Security.Cryptography; using System.Text; using LibHac.Fs; +using LibHac.Util; namespace LibHac.FsSystem { @@ -37,7 +38,7 @@ namespace LibHac.FsSystem var levelData = new IntegrityVerificationStorage(levelInfo[i], Levels[i - 1], integrityCheckLevel, leaveOpen); levelData.GetSize(out long levelSize).ThrowIfFailure(); - int cacheCount = Math.Min((int)Utilities.DivideByRoundUp(levelSize, levelInfo[i].BlockSize), 4); + int cacheCount = Math.Min((int)BitUtil.DivideUp(levelSize, levelInfo[i].BlockSize), 4); Levels[i] = new CachedStorage(levelData, cacheCount, leaveOpen); LevelValidities[i - 1] = levelData.BlockValidities; @@ -145,7 +146,7 @@ namespace LibHac.FsSystem IntegrityVerificationStorage storage = IntegrityStorages[IntegrityStorages.Length - 1]; long blockSize = storage.SectorSize; - int blockCount = (int)Utilities.DivideByRoundUp(Length, blockSize); + int blockCount = (int)BitUtil.DivideUp(Length, blockSize); var buffer = new byte[blockSize]; var result = Validity.Valid; diff --git a/src/LibHac/FsSystem/NcaUtils/Nca.cs b/src/LibHac/FsSystem/NcaUtils/Nca.cs index 5a22b554..82b8cd26 100644 --- a/src/LibHac/FsSystem/NcaUtils/Nca.cs +++ b/src/LibHac/FsSystem/NcaUtils/Nca.cs @@ -150,7 +150,7 @@ namespace LibHac.FsSystem.NcaUtils BaseStorage.GetSize(out long baseSize).ThrowIfFailure(); - if (!Utilities.IsSubRange(offset, size, baseSize)) + if (!IsSubRange(offset, size, baseSize)) { throw new InvalidDataException( $"Section offset (0x{offset:x}) and length (0x{size:x}) fall outside the total NCA length (0x{baseSize:x})."); @@ -693,7 +693,7 @@ namespace LibHac.FsSystem.NcaUtils bodyStorage.GetSize(out long baseSize).ThrowIfFailure(); - if (!Utilities.IsSubRange(offset, size, baseSize)) + if (!IsSubRange(offset, size, baseSize)) { throw new InvalidDataException( $"Section offset (0x{offset + 0x400:x}) and length (0x{size:x}) fall outside the total NCA length (0x{baseSize + 0x400:x})."); @@ -763,5 +763,11 @@ namespace LibHac.FsSystem.NcaUtils header.CounterType = counterType; header.CounterVersion = counterVersion; } + + private static bool IsSubRange(long startIndex, long subLength, long length) + { + bool isOutOfRange = startIndex < 0 || startIndex > length || subLength < 0 || startIndex > length - subLength; + return !isOutOfRange; + } } } diff --git a/src/LibHac/FsSystem/PartitionFileSystemBuilder.cs b/src/LibHac/FsSystem/PartitionFileSystemBuilder.cs index c6249fbd..37c87182 100644 --- a/src/LibHac/FsSystem/PartitionFileSystemBuilder.cs +++ b/src/LibHac/FsSystem/PartitionFileSystemBuilder.cs @@ -7,6 +7,7 @@ using LibHac.Common; using LibHac.Crypto; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem { @@ -121,7 +122,7 @@ namespace LibHac.FsSystem size += entry.NameLength + 1; } - int endOffset = Utilities.AlignUp(startOffset + size, GetMetaDataAlignment(type)); + int endOffset = Alignment.AlignUpPow2(startOffset + size, GetMetaDataAlignment(type)); return endOffset - startOffset; } @@ -135,7 +136,7 @@ namespace LibHac.FsSystem } } - private int GetMetaDataAlignment(PartitionFileSystemType type) + private uint GetMetaDataAlignment(PartitionFileSystemType type) { switch (type) { diff --git a/src/LibHac/FsSystem/RomFs/RomFsBuilder.cs b/src/LibHac/FsSystem/RomFs/RomFsBuilder.cs index 0012f65b..7f4661f3 100644 --- a/src/LibHac/FsSystem/RomFs/RomFsBuilder.cs +++ b/src/LibHac/FsSystem/RomFs/RomFsBuilder.cs @@ -5,6 +5,7 @@ using System.Linq; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem.RomFs { @@ -61,7 +62,7 @@ namespace LibHac.FsSystem.RomFs Sources.Add(fileStorage); long newOffset = CurrentOffset + fileSize; - CurrentOffset = Utilities.AlignUp(newOffset, FileAlignment); + CurrentOffset = Alignment.AlignUp(newOffset, FileAlignment); var padding = new NullStorage(CurrentOffset - newOffset); Sources.Add(padding); diff --git a/src/LibHac/FsSystem/RomFs/RomFsDictionary.cs b/src/LibHac/FsSystem/RomFs/RomFsDictionary.cs index 1fca8c10..31ca7412 100644 --- a/src/LibHac/FsSystem/RomFs/RomFsDictionary.cs +++ b/src/LibHac/FsSystem/RomFs/RomFsDictionary.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Fs; +using LibHac.Util; namespace LibHac.FsSystem.RomFs { @@ -140,7 +141,7 @@ namespace LibHac.FsSystem.RomFs private int CreateNewEntry(int nameLength) { - int bytesNeeded = Utilities.AlignUp(_sizeOfEntry + nameLength, 4); + int bytesNeeded = Alignment.AlignUp(_sizeOfEntry + nameLength, 4); if (_length + bytesNeeded > _capacity) { diff --git a/src/LibHac/FsSystem/Save/AllocationTableStorage.cs b/src/LibHac/FsSystem/Save/AllocationTableStorage.cs index 7af18a5f..44cc3728 100644 --- a/src/LibHac/FsSystem/Save/AllocationTableStorage.cs +++ b/src/LibHac/FsSystem/Save/AllocationTableStorage.cs @@ -1,6 +1,7 @@ using System; using System.IO; using LibHac.Fs; +using LibHac.Util; namespace LibHac.FsSystem.Save { @@ -104,8 +105,8 @@ namespace LibHac.FsSystem.Save protected override Result DoSetSize(long size) { - int oldBlockCount = (int)Utilities.DivideByRoundUp(_length, BlockSize); - int newBlockCount = (int)Utilities.DivideByRoundUp(size, BlockSize); + int oldBlockCount = (int)BitUtil.DivideUp(_length, BlockSize); + int newBlockCount = (int)BitUtil.DivideUp(size, BlockSize); if (oldBlockCount == newBlockCount) return Result.Success; diff --git a/src/LibHac/FsSystem/Save/JournalMap.cs b/src/LibHac/FsSystem/Save/JournalMap.cs index 91666491..91ddb13f 100644 --- a/src/LibHac/FsSystem/Save/JournalMap.cs +++ b/src/LibHac/FsSystem/Save/JournalMap.cs @@ -1,5 +1,6 @@ using System.IO; using LibHac.Fs; +using LibHac.Util; namespace LibHac.FsSystem.Save { @@ -64,8 +65,8 @@ namespace LibHac.FsSystem.Save int physicalBlockCount = virtualBlockCount + Header.JournalBlockCount; int blockMapLength = virtualBlockCount * MapEntryLength; - int physicalBitmapLength = Utilities.AlignUp(physicalBlockCount, 32) / 8; - int virtualBitmapLength = Utilities.AlignUp(virtualBlockCount, 32) / 8; + int physicalBitmapLength = Alignment.AlignUp(physicalBlockCount, 32) / 8; + int virtualBitmapLength = Alignment.AlignUp(virtualBlockCount, 32) / 8; MapStorage.Slice(blockMapLength).Fill(SaveDataFileSystem.TrimFillValue); FreeBlocks.Slice(physicalBitmapLength).Fill(SaveDataFileSystem.TrimFillValue); diff --git a/src/LibHac/FsSystem/Save/SaveDataFileSystemCore.cs b/src/LibHac/FsSystem/Save/SaveDataFileSystemCore.cs index 6e2de686..3b561c83 100644 --- a/src/LibHac/FsSystem/Save/SaveDataFileSystemCore.cs +++ b/src/LibHac/FsSystem/Save/SaveDataFileSystemCore.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Util; namespace LibHac.FsSystem.Save { @@ -57,7 +58,7 @@ namespace LibHac.FsSystem.Save return Result.Success; } - int blockCount = (int)Utilities.DivideByRoundUp(size, AllocationTable.Header.BlockSize); + int blockCount = (int)BitUtil.DivideUp(size, AllocationTable.Header.BlockSize); int startBlock = AllocationTable.Allocate(blockCount); if (startBlock == -1) diff --git a/src/LibHac/FsSystem/SectorStorage.cs b/src/LibHac/FsSystem/SectorStorage.cs index dc098e15..2861f00d 100644 --- a/src/LibHac/FsSystem/SectorStorage.cs +++ b/src/LibHac/FsSystem/SectorStorage.cs @@ -1,5 +1,6 @@ using System; using LibHac.Fs; +using LibHac.Util; namespace LibHac.FsSystem { @@ -20,7 +21,7 @@ namespace LibHac.FsSystem baseStorage.GetSize(out long baseSize).ThrowIfFailure(); - SectorCount = (int)Utilities.DivideByRoundUp(baseSize, SectorSize); + SectorCount = (int)BitUtil.DivideUp(baseSize, SectorSize); Length = baseSize; LeaveOpen = leaveOpen; @@ -57,7 +58,7 @@ namespace LibHac.FsSystem rc = BaseStorage.GetSize(out long newSize); if (rc.IsFailure()) return rc; - SectorCount = (int)Utilities.DivideByRoundUp(newSize, SectorSize); + SectorCount = (int)BitUtil.DivideUp(newSize, SectorSize); Length = newSize; return Result.Success; diff --git a/src/LibHac/Ticket.cs b/src/LibHac/Ticket.cs index 6b328197..855ad59e 100644 --- a/src/LibHac/Ticket.cs +++ b/src/LibHac/Ticket.cs @@ -1,6 +1,7 @@ using System; using System.IO; using LibHac.Common.Keys; +using LibHac.Util; namespace LibHac { @@ -112,7 +113,7 @@ namespace LibHac throw new ArgumentOutOfRangeException(); } - long bodyStart = Utilities.GetNextMultiple(4 + sigLength, 0x40); + long bodyStart = Alignment.AlignUp(4 + sigLength, 0x40); writer.Write((int)SignatureType); diff --git a/src/LibHac/Util/Alignment.cs b/src/LibHac/Util/Alignment.cs new file mode 100644 index 00000000..3a8901e4 --- /dev/null +++ b/src/LibHac/Util/Alignment.cs @@ -0,0 +1,70 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using LibHac.Diag; + +namespace LibHac.Util +{ + public static class Alignment + { + // The alignment functions in this class come from C++ templates that always cast to unsigned types + + public static ulong AlignUpPow2(ulong value, uint alignment) + { + Assert.AssertTrue(BitUtil.IsPowerOfTwo(alignment)); + + ulong invMask = alignment - 1; + return ((value + invMask) & ~invMask); + } + + public static ulong AlignDownPow2(ulong value, uint alignment) + { + Assert.AssertTrue(BitUtil.IsPowerOfTwo(alignment)); + + ulong invMask = alignment - 1; + return (value & ~invMask); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool IsAlignedPow2(ulong value, uint alignment) + { + Assert.AssertTrue(BitUtil.IsPowerOfTwo(alignment)); + + ulong invMask = alignment - 1; + return (value & invMask) == 0; + } + + public static bool IsAlignedPow2(Span buffer, uint alignment) + { + return IsAlignedPow2(buffer, alignment); + } + + public static bool IsAlignedPow2(ReadOnlySpan buffer, uint alignment) + { + return IsAlignedPow2(ref MemoryMarshal.GetReference(buffer), alignment); + } + + public static unsafe bool IsAlignedPow2(ref T pointer, uint alignment) + { + return IsAlignedPow2((ulong)Unsafe.AsPointer(ref pointer), alignment); + } + + public static int AlignUpPow2(int value, uint alignment) => (int)AlignUpPow2((ulong)value, alignment); + public static long AlignUpPow2(long value, uint alignment) => (long)AlignUpPow2((ulong)value, alignment); + public static int AlignDownPow2(int value, uint alignment) => (int)AlignDownPow2((ulong)value, alignment); + public static long AlignDownPow2(long value, uint alignment) => (long)AlignDownPow2((ulong)value, alignment); + public static bool IsAlignedPow2(int value, uint alignment) => IsAlignedPow2((ulong)value, alignment); + public static bool IsAlignedPow2(long value, uint alignment) => IsAlignedPow2((ulong)value, alignment); + + public static ulong AlignUp(ulong value, uint alignment) => AlignDown(value + alignment - 1, alignment); + public static ulong AlignDown(ulong value, uint alignment) => value - value % alignment; + public static bool IsAligned(ulong value, uint alignment) => value % alignment == 0; + + public static int AlignUp(int value, uint alignment) => (int)AlignUp((ulong)value, alignment); + public static long AlignUp(long value, uint alignment) => (long)AlignUp((ulong)value, alignment); + public static int AlignDown(int value, uint alignment) => (int)AlignDown((ulong)value, alignment); + public static long AlignDown(long value, uint alignment) => (long)AlignDown((ulong)value, alignment); + public static bool IsAligned(int value, uint alignment) => IsAligned((ulong)value, alignment); + public static bool IsAligned(long value, uint alignment) => IsAligned((ulong)value, alignment); + } +} diff --git a/src/LibHac/Util/BitUtil.cs b/src/LibHac/Util/BitUtil.cs new file mode 100644 index 00000000..0af9f02f --- /dev/null +++ b/src/LibHac/Util/BitUtil.cs @@ -0,0 +1,46 @@ +using System.Runtime.CompilerServices; + +namespace LibHac.Util +{ + public static class BitUtil + { + public static bool IsPowerOfTwo(int value) + { + return value > 0 && ResetLeastSignificantOneBit(value) == 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPowerOfTwo(long value) + { + return value > 0 && ResetLeastSignificantOneBit(value) == 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPowerOfTwo(ulong value) + { + return value > 0 && ResetLeastSignificantOneBit(value) == 0; + } + + private static int ResetLeastSignificantOneBit(int value) + { + return value & (value - 1); + } + + private static long ResetLeastSignificantOneBit(long value) + { + return value & (value - 1); + } + + private static ulong ResetLeastSignificantOneBit(ulong value) + { + return value & (value - 1); + } + + // DivideUp comes from a C++ template that always casts to unsigned types + public static uint DivideUp(uint value, uint divisor) => (value + divisor - 1) / divisor; + public static ulong DivideUp(ulong value, ulong divisor) => (value + divisor - 1) / divisor; + + public static int DivideUp(int value, int divisor) => (int)DivideUp((uint)value, (uint)divisor); + public static long DivideUp(long value, long divisor) => (long)DivideUp((ulong)value, (ulong)divisor); + } +} diff --git a/src/LibHac/Utilities.cs b/src/LibHac/Utilities.cs index 25c29df8..ea758540 100644 --- a/src/LibHac/Utilities.cs +++ b/src/LibHac/Utilities.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Numerics; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -11,35 +10,6 @@ namespace LibHac { private const int MediaSize = 0x200; - public static byte[][] CreateJaggedByteArray(int len1, int len2) - { - var array = new byte[len1][]; - - for (int i = 0; i < array.Length; i++) - { - array[i] = new byte[len2]; - } - - return array; - } - - public static byte[][][] CreateJaggedByteArray(int len1, int len2, int len3) - { - var array = new byte[len1][][]; - - for (int i = 0; i < array.Length; i++) - { - array[i] = new byte[len2][]; - - for (int j = 0; j < array[i].Length; j++) - { - array[i][j] = new byte[len3]; - } - } - - return array; - } - public static bool ArraysEqual(T[] a1, T[] a2) { if (a1 == null || a2 == null) return false; @@ -265,26 +235,7 @@ namespace LibHac // Return formatted number with suffix return readable.ToString("0.### ") + suffix; } - - public static long GetNextMultiple(long value, int multiple) - { - if (multiple <= 0) - return value; - - if (value % multiple == 0) - return value; - - return value + multiple - value % multiple; - } - - public static int DivideByRoundUp(int value, int divisor) => (value + divisor - 1) / divisor; - public static long DivideByRoundUp(long value, long divisor) => (value + divisor - 1) / divisor; - - public static int AlignUp(int value, int multiple) => AlignDown(value + multiple - 1, multiple); - public static long AlignUp(long value, long multiple) => AlignDown(value + multiple - 1, multiple); - public static int AlignDown(int value, int multiple) => value - value % multiple; - public static long AlignDown(long value, long multiple) => value - value % multiple; - + public static void IncrementByteArray(byte[] array) { for (int i = array.Length - 1; i >= 0; i--) @@ -341,100 +292,11 @@ namespace LibHac _ => "Unknown" }; - public static bool IsSubRange(long startIndex, long subLength, long length) - { - bool isOutOfRange = startIndex < 0 || startIndex > length || subLength < 0 || startIndex > length - subLength; - return !isOutOfRange; - } - public static int GetMasterKeyRevision(int keyGeneration) { if (keyGeneration == 0) return 0; return keyGeneration - 1; } - - public static bool IsPowerOfTwo(int value) - { - return value > 0 && ResetLeastSignificantOneBit(value) == 0; - } - - public static bool IsPowerOfTwo(long value) - { - return value > 0 && ResetLeastSignificantOneBit(value) == 0; - } - - public static BigInteger GetBigInteger(this ReadOnlySpan bytes) - { - var signPadded = new byte[bytes.Length + 1]; - bytes.CopyTo(signPadded.AsSpan(1)); - Array.Reverse(signPadded); - return new BigInteger(signPadded); - } - - public static byte[] GetBytes(this BigInteger value, int size) - { - byte[] bytes = value.ToByteArray(); - - if (size == -1) - { - size = bytes.Length; - } - - if (bytes.Length > size + 1) - { - throw new InvalidOperationException($"Cannot squeeze value {value} to {size} bytes from {bytes.Length}."); - } - - if (bytes.Length == size + 1 && bytes[bytes.Length - 1] != 0) - { - throw new InvalidOperationException($"Cannot squeeze value {value} to {size} bytes from {bytes.Length}."); - } - - Array.Resize(ref bytes, size); - Array.Reverse(bytes); - return bytes; - } - - public static BigInteger ModInverse(BigInteger e, BigInteger n) - { - BigInteger r = n; - BigInteger newR = e; - BigInteger t = 0; - BigInteger newT = 1; - - while (newR != 0) - { - BigInteger quotient = r / newR; - BigInteger temp; - - temp = t; - t = newT; - newT = temp - quotient * newT; - - temp = r; - r = newR; - newR = temp - quotient * newR; - } - - if (t < 0) - { - t = t + n; - } - - return t; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int ResetLeastSignificantOneBit(int value) - { - return value & (value - 1); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static long ResetLeastSignificantOneBit(long value) - { - return value & (value - 1); - } } }