aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Horizon/Sdk/Ngc/Detail/CompressedArray.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2023-09-27 14:21:26 -0300
committerGitHub <noreply@github.com>2023-09-27 19:21:26 +0200
commit01c2b8097c2d66839105470d82405a12d57d196f (patch)
tree466e1a04138bd14ba31a6a0738a46065b6033129 /src/Ryujinx.Horizon/Sdk/Ngc/Detail/CompressedArray.cs
parent4bd2ca3f0de37c53b3ecc78789a0a8296668235a (diff)
Implement NGC service (#5681)
* Implement NGC service * Use raw byte arrays instead of string for _wordSeparators * Silence IDE0230 for _wordSeparators * Try to silence warning about _rangeValuesCount not being read on SparseSet * Make AcType enum private * Add abstract methods and one TODO that I forgot * PR feedback * More PR feedback * More PR feedback
Diffstat (limited to 'src/Ryujinx.Horizon/Sdk/Ngc/Detail/CompressedArray.cs')
-rw-r--r--src/Ryujinx.Horizon/Sdk/Ngc/Detail/CompressedArray.cs100
1 files changed, 100 insertions, 0 deletions
diff --git a/src/Ryujinx.Horizon/Sdk/Ngc/Detail/CompressedArray.cs b/src/Ryujinx.Horizon/Sdk/Ngc/Detail/CompressedArray.cs
new file mode 100644
index 00000000..1200f1de
--- /dev/null
+++ b/src/Ryujinx.Horizon/Sdk/Ngc/Detail/CompressedArray.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Horizon.Sdk.Ngc.Detail
+{
+ class CompressedArray
+ {
+ private const int MaxUncompressedEntries = 64;
+ private const int CompressedEntriesPerBlock = 64;
+ private const int BitsPerWord = Set.BitsPerWord;
+
+ private readonly struct BitfieldRange
+ {
+ private readonly uint _range;
+ private readonly int _baseValue;
+
+ public int BitfieldIndex => (int)(_range & 0x7ffffff);
+ public int BitfieldLength => (int)(_range >> 27) + 1;
+ public int BaseValue => _baseValue;
+
+ public BitfieldRange(uint range, int baseValue)
+ {
+ _range = range;
+ _baseValue = baseValue;
+ }
+ }
+
+ private uint[] _bitfieldRanges;
+ private uint[] _bitfields;
+ private int[] _uncompressedArray;
+
+ public int Length => (_bitfieldRanges.Length / 2) * CompressedEntriesPerBlock + _uncompressedArray.Length;
+
+ public int this[int index]
+ {
+ get
+ {
+ var ranges = GetBitfieldRanges();
+
+ int rangeBlockIndex = index / CompressedEntriesPerBlock;
+
+ if (rangeBlockIndex < ranges.Length)
+ {
+ var range = ranges[rangeBlockIndex];
+
+ int bitfieldLength = range.BitfieldLength;
+ int bitfieldOffset = (index % CompressedEntriesPerBlock) * bitfieldLength;
+ int bitfieldIndex = range.BitfieldIndex + (bitfieldOffset / BitsPerWord);
+ int bitOffset = bitfieldOffset % BitsPerWord;
+
+ ulong bitfieldValue = _bitfields[bitfieldIndex];
+
+ // If the bit fields crosses the word boundary, let's load the next one to ensure we
+ // have access to the full value.
+ if (bitOffset + bitfieldLength > BitsPerWord)
+ {
+ bitfieldValue |= (ulong)_bitfields[bitfieldIndex + 1] << 32;
+ }
+
+ int value = (int)(bitfieldValue >> bitOffset) & ((1 << bitfieldLength) - 1);
+
+ // Sign-extend.
+ int remainderBits = BitsPerWord - bitfieldLength;
+ value <<= remainderBits;
+ value >>= remainderBits;
+
+ return value + range.BaseValue;
+ }
+ else if (rangeBlockIndex < _uncompressedArray.Length + _bitfieldRanges.Length * BitsPerWord)
+ {
+ return _uncompressedArray[index % MaxUncompressedEntries];
+ }
+
+ return 0;
+ }
+ }
+
+ private ReadOnlySpan<BitfieldRange> GetBitfieldRanges()
+ {
+ return MemoryMarshal.Cast<uint, BitfieldRange>(_bitfieldRanges);
+ }
+
+ public bool Import(ref BinaryReader reader)
+ {
+ if (!reader.Read(out int bitfieldRangesCount) ||
+ reader.AllocateAndReadArray(ref _bitfieldRanges, bitfieldRangesCount) != bitfieldRangesCount)
+ {
+ return false;
+ }
+
+ if (!reader.Read(out int bitfieldsCount) || reader.AllocateAndReadArray(ref _bitfields, bitfieldsCount) != bitfieldsCount)
+ {
+ return false;
+ }
+
+ return reader.Read(out byte uncompressedArrayLength) &&
+ reader.AllocateAndReadArray(ref _uncompressedArray, uncompressedArrayLength, MaxUncompressedEntries) == uncompressedArrayLength;
+ }
+ }
+}