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 GetBitfieldRanges() { return MemoryMarshal.Cast(_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; } } }