diff --git a/Penumbra.GameData b/Penumbra.GameData index 3d4d8510..2ff50e68 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 3d4d8510f832dfd95d7069b86e6b3da4ec612558 +Subproject commit 2ff50e68f7c951f0f8b25957a400a2e32ed9d6dc diff --git a/Penumbra/Api/Api/ModSettingsApi.cs b/Penumbra/Api/Api/ModSettingsApi.cs index d49c2904..3ba17cf4 100644 --- a/Penumbra/Api/Api/ModSettingsApi.cs +++ b/Penumbra/Api/Api/ModSettingsApi.cs @@ -73,27 +73,6 @@ public class ModSettingsApi : IPenumbraApiModSettings, IApiService, IDisposable return (ret.Item1, (ret.Item2.Value.Item1, ret.Item2.Value.Item2, ret.Item2.Value.Item3, ret.Item2.Value.Item4)); } - public PenumbraApiEc GetSettingsInAllCollections(string modDirectory, string modName, - out Dictionary>, bool, bool)> settings, - bool ignoreTemporaryCollections = false) - { - settings = []; - if (!_modManager.TryGetMod(modDirectory, modName, out var mod)) - return PenumbraApiEc.ModMissing; - - var collections = ignoreTemporaryCollections - ? _collectionManager.Storage.Where(c => c != ModCollection.Empty) - : _collectionManager.Storage.Where(c => c != ModCollection.Empty).Concat(_collectionManager.Temp.Values); - settings = []; - foreach (var collection in collections) - { - if (GetCurrentSettings(collection, mod, false, false, 0) is { } s) - settings.Add(collection.Identity.Id, s); - } - - return PenumbraApiEc.Success; - } - public (PenumbraApiEc, (bool, int, Dictionary>, bool, bool)?) GetCurrentModSettingsWithTemp(Guid collectionId, string modDirectory, string modName, bool ignoreInheritance, bool ignoreTemporary, int key) { diff --git a/Penumbra/Api/Api/ResolveApi.cs b/Penumbra/Api/Api/ResolveApi.cs index 00a0c86f..481ea7ad 100644 --- a/Penumbra/Api/Api/ResolveApi.cs +++ b/Penumbra/Api/Api/ResolveApi.cs @@ -1,6 +1,5 @@ using Dalamud.Plugin.Services; using OtterGui.Services; -using Penumbra.Api.Enums; using Penumbra.Collections; using Penumbra.Collections.Manager; using Penumbra.Interop.PathResolving; @@ -42,19 +41,6 @@ public class ResolveApi( return ret.Select(r => r.ToString()).ToArray(); } - public PenumbraApiEc ResolvePath(Guid collectionId, string gamePath, out string resolvedPath) - { - resolvedPath = gamePath; - if (!collectionManager.Storage.ById(collectionId, out var collection)) - return PenumbraApiEc.CollectionMissing; - - if (!collection.HasCache) - return PenumbraApiEc.CollectionInactive; - - resolvedPath = ResolvePath(gamePath, modManager, collection); - return PenumbraApiEc.Success; - } - public string[] ReverseResolvePlayerPath(string moddedPath) { if (!config.EnableMods) @@ -78,26 +64,6 @@ public class ResolveApi( return (resolved, reverseResolved.Select(a => a.Select(p => p.ToString()).ToArray()).ToArray()); } - public PenumbraApiEc ResolvePaths(Guid collectionId, string[] forward, string[] reverse, out string[] resolvedForward, - out string[][] resolvedReverse) - { - resolvedForward = forward; - resolvedReverse = []; - if (!config.EnableMods) - return PenumbraApiEc.Success; - - if (!collectionManager.Storage.ById(collectionId, out var collection)) - return PenumbraApiEc.CollectionMissing; - - if (!collection.HasCache) - return PenumbraApiEc.CollectionInactive; - - resolvedForward = forward.Select(p => ResolvePath(p, modManager, collection)).ToArray(); - var reverseResolved = collection.ReverseResolvePaths(reverse); - resolvedReverse = reverseResolved.Select(a => a.Select(p => p.ToString()).ToArray()).ToArray(); - return PenumbraApiEc.Success; - } - public async Task<(string[], string[][])> ResolvePlayerPathsAsync(string[] forward, string[] reverse) { if (!config.EnableMods) diff --git a/Penumbra/Api/Api/UiApi.cs b/Penumbra/Api/Api/UiApi.cs index 70f018bb..b14f67ae 100644 --- a/Penumbra/Api/Api/UiApi.cs +++ b/Penumbra/Api/Api/UiApi.cs @@ -81,12 +81,6 @@ public class UiApi : IPenumbraApiUi, IApiService, IDisposable public void CloseMainWindow() => _configWindow.IsOpen = false; - public PenumbraApiEc RegisterSettingsSection(Action draw) - => throw new NotImplementedException(); - - public PenumbraApiEc UnregisterSettingsSection(Action draw) - => throw new NotImplementedException(); - private void OnChangedItemClick(MouseButton button, IIdentifiedObjectData data) { if (ChangedItemClicked == null) diff --git a/Penumbra/Api/IpcProviders.cs b/Penumbra/Api/IpcProviders.cs index fdacc73b..5f04540f 100644 --- a/Penumbra/Api/IpcProviders.cs +++ b/Penumbra/Api/IpcProviders.cs @@ -66,7 +66,6 @@ public sealed class IpcProviders : IDisposable, IApiService IpcSubscribers.GetCurrentModSettings.Provider(pi, api.ModSettings), IpcSubscribers.GetCurrentModSettingsWithTemp.Provider(pi, api.ModSettings), IpcSubscribers.GetAllModSettings.Provider(pi, api.ModSettings), - IpcSubscribers.GetSettingsInAllCollections.Provider(pi, api.ModSettings), IpcSubscribers.TryInheritMod.Provider(pi, api.ModSettings), IpcSubscribers.TrySetMod.Provider(pi, api.ModSettings), IpcSubscribers.TrySetModPriority.Provider(pi, api.ModSettings), @@ -99,8 +98,6 @@ public sealed class IpcProviders : IDisposable, IApiService IpcSubscribers.ReverseResolvePlayerPath.Provider(pi, api.Resolve), IpcSubscribers.ResolvePlayerPaths.Provider(pi, api.Resolve), IpcSubscribers.ResolvePlayerPathsAsync.Provider(pi, api.Resolve), - IpcSubscribers.ResolvePath.Provider(pi, api.Resolve), - IpcSubscribers.ResolvePaths.Provider(pi, api.Resolve), IpcSubscribers.GetGameObjectResourcePaths.Provider(pi, api.ResourceTree), IpcSubscribers.GetPlayerResourcePaths.Provider(pi, api.ResourceTree), diff --git a/Penumbra/Import/Textures/TextureManager.cs b/Penumbra/Import/Textures/TextureManager.cs index 177722ec..073fef2f 100644 --- a/Penumbra/Import/Textures/TextureManager.cs +++ b/Penumbra/Import/Textures/TextureManager.cs @@ -7,12 +7,12 @@ using OtterGui.Log; using OtterGui.Services; using OtterGui.Tasks; using OtterTex; +using SharpDX.Direct3D11; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.PixelFormats; -using TerraFX.Interop.DirectX; -using TerraFX.Interop.Windows; +using DxgiDevice = SharpDX.DXGI.Device; using Image = SixLabors.ImageSharp.Image; namespace Penumbra.Import.Textures; @@ -125,11 +125,11 @@ public sealed class TextureManager(IDataManager gameData, Logger logger, ITextur switch (_type) { case TextureType.Png: - data?.SaveAsync(_outputPath, new PngEncoder { CompressionLevel = PngCompressionLevel.NoCompression }, cancel) + data?.SaveAsync(_outputPath, new PngEncoder() { CompressionLevel = PngCompressionLevel.NoCompression }, cancel) .Wait(cancel); return; case TextureType.Targa: - data?.SaveAsync(_outputPath, new TgaEncoder + data?.SaveAsync(_outputPath, new TgaEncoder() { Compression = TgaCompression.None, BitsPerPixel = TgaBitsPerPixel.Pixel32, @@ -204,16 +204,11 @@ public sealed class TextureManager(IDataManager gameData, Logger logger, ITextur rgba, width, height), CombinedTexture.TextureSaveType.AsIs when imageTypeBehaviour is TextureType.Dds => AddMipMaps(image.AsDds!, _mipMaps), CombinedTexture.TextureSaveType.Bitmap => ConvertToRgbaDds(image, _mipMaps, cancel, rgba, width, height), - CombinedTexture.TextureSaveType.BC1 => _textures.ConvertToCompressedDds(image, _mipMaps, DXGIFormat.BC1UNorm, cancel, rgba, - width, height), - CombinedTexture.TextureSaveType.BC3 => _textures.ConvertToCompressedDds(image, _mipMaps, DXGIFormat.BC3UNorm, cancel, rgba, - width, height), - CombinedTexture.TextureSaveType.BC4 => _textures.ConvertToCompressedDds(image, _mipMaps, DXGIFormat.BC4UNorm, cancel, rgba, - width, height), - CombinedTexture.TextureSaveType.BC5 => _textures.ConvertToCompressedDds(image, _mipMaps, DXGIFormat.BC5UNorm, cancel, rgba, - width, height), - CombinedTexture.TextureSaveType.BC7 => _textures.ConvertToCompressedDds(image, _mipMaps, DXGIFormat.BC7UNorm, cancel, rgba, - width, height), + CombinedTexture.TextureSaveType.BC1 => _textures.ConvertToCompressedDds(image, _mipMaps, DXGIFormat.BC1UNorm, cancel, rgba, width, height), + CombinedTexture.TextureSaveType.BC3 => _textures.ConvertToCompressedDds(image, _mipMaps, DXGIFormat.BC3UNorm, cancel, rgba, width, height), + CombinedTexture.TextureSaveType.BC4 => _textures.ConvertToCompressedDds(image, _mipMaps, DXGIFormat.BC4UNorm, cancel, rgba, width, height), + CombinedTexture.TextureSaveType.BC5 => _textures.ConvertToCompressedDds(image, _mipMaps, DXGIFormat.BC5UNorm, cancel, rgba, width, height), + CombinedTexture.TextureSaveType.BC7 => _textures.ConvertToCompressedDds(image, _mipMaps, DXGIFormat.BC7UNorm, cancel, rgba, width, height), _ => throw new Exception("Wrong save type."), }; @@ -395,7 +390,7 @@ public sealed class TextureManager(IDataManager gameData, Logger logger, ITextur } /// Create a BC3 or BC7 block-compressed .dds from the input (optionally with mipmaps). Returns input (+ mipmaps) if it is already the correct format. - public unsafe ScratchImage CreateCompressed(ScratchImage input, bool mipMaps, DXGIFormat format, CancellationToken cancel) + public ScratchImage CreateCompressed(ScratchImage input, bool mipMaps, DXGIFormat format, CancellationToken cancel) { if (input.Meta.Format == format) return input; @@ -411,58 +406,11 @@ public sealed class TextureManager(IDataManager gameData, Logger logger, ITextur // See https://github.com/microsoft/DirectXTex/wiki/Compress#parameters for the format condition. if (format is DXGIFormat.BC6HUF16 or DXGIFormat.BC6HSF16 or DXGIFormat.BC7UNorm or DXGIFormat.BC7UNormSRGB) { - ref var device = ref *(ID3D11Device*)uiBuilder.DeviceHandle; - IDXGIDevice* dxgiDevice; - Marshal.ThrowExceptionForHR(device.QueryInterface(TerraFX.Interop.Windows.Windows.__uuidof(), (void**)&dxgiDevice)); + var device = new Device(uiBuilder.DeviceHandle); + var dxgiDevice = device.QueryInterface(); - try - { - IDXGIAdapter* adapter = null; - Marshal.ThrowExceptionForHR(dxgiDevice->GetAdapter(&adapter)); - try - { - dxgiDevice->Release(); - dxgiDevice = null; - - ID3D11Device* deviceClone = null; - ID3D11DeviceContext* contextClone = null; - var featureLevel = device.GetFeatureLevel(); - Marshal.ThrowExceptionForHR(DirectX.D3D11CreateDevice( - adapter, - D3D_DRIVER_TYPE.D3D_DRIVER_TYPE_UNKNOWN, - HMODULE.NULL, - device.GetCreationFlags(), - &featureLevel, - 1, - D3D11.D3D11_SDK_VERSION, - &deviceClone, - null, - &contextClone)); - try - { - adapter->Release(); - adapter = null; - return input.Compress((nint)deviceClone, format, CompressFlags.Parallel); - } - finally - { - if (contextClone is not null) - contextClone->Release(); - if (deviceClone is not null) - deviceClone->Release(); - } - } - finally - { - if (adapter is not null) - adapter->Release(); - } - } - finally - { - if (dxgiDevice is not null) - dxgiDevice->Release(); - } + using var deviceClone = new Device(dxgiDevice.Adapter, device.CreationFlags, device.FeatureLevel); + return input.Compress(deviceClone.NativePointer, format, CompressFlags.Parallel); } return input.Compress(format, CompressFlags.BC7Quick | CompressFlags.Parallel); @@ -508,7 +456,7 @@ public sealed class TextureManager(IDataManager gameData, Logger logger, ITextur GC.KeepAlive(input); } - private readonly struct ImageInputData : IEquatable + private readonly struct ImageInputData { private readonly string? _inputPath; @@ -576,8 +524,5 @@ public sealed class TextureManager(IDataManager gameData, Logger logger, ITextur public override int GetHashCode() => _inputPath != null ? _inputPath.ToLowerInvariant().GetHashCode() : HashCode.Combine(_width, _height); - - public override bool Equals(object? obj) - => obj is ImageInputData o && Equals(o); } } diff --git a/Penumbra/Interop/Services/TextureArraySlicer.cs b/Penumbra/Interop/Services/TextureArraySlicer.cs index 7b873f26..11498878 100644 --- a/Penumbra/Interop/Services/TextureArraySlicer.cs +++ b/Penumbra/Interop/Services/TextureArraySlicer.cs @@ -1,7 +1,8 @@ using Dalamud.Bindings.ImGui; using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using OtterGui.Services; -using TerraFX.Interop.DirectX; +using SharpDX.Direct3D; +using SharpDX.Direct3D11; namespace Penumbra.Interop.Services; @@ -21,78 +22,46 @@ public sealed unsafe class TextureArraySlicer : IUiService, IDisposable if (texture == null) throw new ArgumentNullException(nameof(texture)); if (sliceIndex >= texture->ArraySize) - throw new ArgumentOutOfRangeException(nameof(sliceIndex), - $"Slice index ({sliceIndex}) is greater than or equal to the texture array size ({texture->ArraySize})"); - + throw new ArgumentOutOfRangeException(nameof(sliceIndex), $"Slice index ({sliceIndex}) is greater than or equal to the texture array size ({texture->ArraySize})"); if (_activeSlices.TryGetValue(((nint)texture, sliceIndex), out var state)) { state.Refresh(); return new ImTextureID((nint)state.ShaderResourceView); } - - ref var srv = ref *(ID3D11ShaderResourceView*)(nint)texture->D3D11ShaderResourceView; - srv.AddRef(); - try + var srv = (ShaderResourceView)(nint)texture->D3D11ShaderResourceView; + var description = srv.Description; + switch (description.Dimension) { - D3D11_SHADER_RESOURCE_VIEW_DESC description; - srv.GetDesc(&description); - switch (description.ViewDimension) - { - case D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE1D: - case D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE2D: - case D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE2DMS: - case D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE3D: - case D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURECUBE: - // This function treats these as single-slice arrays. - // As per the range check above, the only valid slice (i. e. 0) has been requested, therefore there is nothing to do. - break; - case D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE1DARRAY: - description.Texture1DArray.FirstArraySlice = sliceIndex; - description.Texture2DArray.ArraySize = 1; - break; - case D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE2DARRAY: - description.Texture2DArray.FirstArraySlice = sliceIndex; - description.Texture2DArray.ArraySize = 1; - break; - case D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY: - description.Texture2DMSArray.FirstArraySlice = sliceIndex; - description.Texture2DMSArray.ArraySize = 1; - break; - case D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURECUBEARRAY: - description.TextureCubeArray.First2DArrayFace = sliceIndex * 6u; - description.TextureCubeArray.NumCubes = 1; - break; - default: - throw new NotSupportedException($"{nameof(TextureArraySlicer)} does not support dimension {description.ViewDimension}"); - } - - ID3D11Device* device = null; - srv.GetDevice(&device); - ID3D11Resource* resource = null; - srv.GetResource(&resource); - try - { - ID3D11ShaderResourceView* slicedSrv = null; - Marshal.ThrowExceptionForHR(device->CreateShaderResourceView(resource, &description, &slicedSrv)); - resource->Release(); - device->Release(); - - state = new SliceState(slicedSrv); - _activeSlices.Add(((nint)texture, sliceIndex), state); - return new ImTextureID((nint)state.ShaderResourceView); - } - finally - { - if (resource is not null) - resource->Release(); - if (device is not null) - device->Release(); - } - } - finally - { - srv.Release(); + case ShaderResourceViewDimension.Texture1D: + case ShaderResourceViewDimension.Texture2D: + case ShaderResourceViewDimension.Texture2DMultisampled: + case ShaderResourceViewDimension.Texture3D: + case ShaderResourceViewDimension.TextureCube: + // This function treats these as single-slice arrays. + // As per the range check above, the only valid slice (i. e. 0) has been requested, therefore there is nothing to do. + break; + case ShaderResourceViewDimension.Texture1DArray: + description.Texture1DArray.FirstArraySlice = sliceIndex; + description.Texture2DArray.ArraySize = 1; + break; + case ShaderResourceViewDimension.Texture2DArray: + description.Texture2DArray.FirstArraySlice = sliceIndex; + description.Texture2DArray.ArraySize = 1; + break; + case ShaderResourceViewDimension.Texture2DMultisampledArray: + description.Texture2DMSArray.FirstArraySlice = sliceIndex; + description.Texture2DMSArray.ArraySize = 1; + break; + case ShaderResourceViewDimension.TextureCubeArray: + description.TextureCubeArray.First2DArrayFace = sliceIndex * 6; + description.TextureCubeArray.CubeCount = 1; + break; + default: + throw new NotSupportedException($"{nameof(TextureArraySlicer)} does not support dimension {description.Dimension}"); } + state = new SliceState(new ShaderResourceView(srv.Device, srv.Resource, description)); + _activeSlices.Add(((nint)texture, sliceIndex), state); + return new ImTextureID((nint)state.ShaderResourceView); } public void Tick() @@ -104,9 +73,10 @@ public sealed unsafe class TextureArraySlicer : IUiService, IDisposable if (!slice.Tick()) _expiredKeys.Add(key); } - foreach (var key in _expiredKeys) + { _activeSlices.Remove(key); + } } finally { @@ -117,12 +87,14 @@ public sealed unsafe class TextureArraySlicer : IUiService, IDisposable public void Dispose() { foreach (var slice in _activeSlices.Values) + { slice.Dispose(); + } } - private sealed class SliceState(ID3D11ShaderResourceView* shaderResourceView) : IDisposable + private sealed class SliceState(ShaderResourceView shaderResourceView) : IDisposable { - public readonly ID3D11ShaderResourceView* ShaderResourceView = shaderResourceView; + public readonly ShaderResourceView ShaderResourceView = shaderResourceView; private uint _timeToLive = InitialTimeToLive; @@ -136,15 +108,13 @@ public sealed unsafe class TextureArraySlicer : IUiService, IDisposable if (unchecked(_timeToLive--) > 0) return true; - if (ShaderResourceView is not null) - ShaderResourceView->Release(); + ShaderResourceView.Dispose(); return false; } public void Dispose() { - if (ShaderResourceView is not null) - ShaderResourceView->Release(); + ShaderResourceView.Dispose(); } } } diff --git a/Penumbra/Penumbra.csproj b/Penumbra/Penumbra.csproj index 43f853f3..f04928a5 100644 --- a/Penumbra/Penumbra.csproj +++ b/Penumbra/Penumbra.csproj @@ -38,8 +38,16 @@ $(DalamudLibPath)Iced.dll False - - $(DalamudLibPath)TerraFX.Interop.Windows.dll + + $(DalamudLibPath)SharpDX.dll + False + + + $(DalamudLibPath)SharpDX.Direct3D11.dll + False + + + $(DalamudLibPath)SharpDX.DXGI.dll False diff --git a/Penumbra/Services/StaticServiceManager.cs b/Penumbra/Services/StaticServiceManager.cs index be482d1d..27582395 100644 --- a/Penumbra/Services/StaticServiceManager.cs +++ b/Penumbra/Services/StaticServiceManager.cs @@ -48,7 +48,6 @@ public static class StaticServiceManager .AddDalamudService(pi) .AddDalamudService(pi) .AddDalamudService(pi) - .AddDalamudService(pi) .AddDalamudService(pi) .AddDalamudService(pi) .AddDalamudService(pi) diff --git a/Penumbra/UI/Tabs/Debug/GlobalVariablesDrawer.cs b/Penumbra/UI/Tabs/Debug/GlobalVariablesDrawer.cs index bc5f0765..f0ab1125 100644 --- a/Penumbra/UI/Tabs/Debug/GlobalVariablesDrawer.cs +++ b/Penumbra/UI/Tabs/Debug/GlobalVariablesDrawer.cs @@ -9,7 +9,6 @@ using OtterGui.Text; using Penumbra.Interop.Services; using Penumbra.Interop.Structs; using Penumbra.String; -using Penumbra.Util; using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager; namespace Penumbra.UI.Tabs.Debug; @@ -179,10 +178,10 @@ public unsafe class GlobalVariablesDrawer( if (_schedulerFilterMap.Length is 0 || resource->Name.Buffer.IndexOf(_schedulerFilterMapU8.Span) >= 0) { ImUtf8.DrawTableColumn($"[{total:D4}]"); - ImUtf8.DrawTableColumn($"{resource->Name.GetField(16)}"); // Unk1 + ImUtf8.DrawTableColumn($"{resource->Name.Unk1}"); ImUtf8.DrawTableColumn(new CiByteString(resource->Name.Buffer, MetaDataComputation.None).Span); ImUtf8.DrawTableColumn($"{resource->Consumers}"); - ImUtf8.DrawTableColumn($"{PointerExtensions.GetField(resource, 120)}"); // key, Unk1 + ImUtf8.DrawTableColumn($"{resource->Unk1}"); // key ImGui.TableNextColumn(); Penumbra.Dynamis.DrawPointer(resource); ImGui.TableNextColumn(); @@ -228,10 +227,10 @@ public unsafe class GlobalVariablesDrawer( if (_schedulerFilterList.Length is 0 || resource->Name.Buffer.IndexOf(_schedulerFilterListU8.Span) >= 0) { ImUtf8.DrawTableColumn($"[{total:D4}]"); - ImUtf8.DrawTableColumn($"{resource->Name.GetField(16)}"); // Unk1 + ImUtf8.DrawTableColumn($"{resource->Name.Unk1}"); ImUtf8.DrawTableColumn(new CiByteString(resource->Name.Buffer, MetaDataComputation.None).Span); ImUtf8.DrawTableColumn($"{resource->Consumers}"); - ImUtf8.DrawTableColumn($"{PointerExtensions.GetField(resource, 120)}"); // key, Unk1 + ImUtf8.DrawTableColumn($"{resource->Unk1}"); // key ImGui.TableNextColumn(); Penumbra.Dynamis.DrawPointer(resource); ImGui.TableNextColumn(); diff --git a/Penumbra/Util/PointerExtensions.cs b/Penumbra/Util/PointerExtensions.cs deleted file mode 100644 index c70e2177..00000000 --- a/Penumbra/Util/PointerExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Penumbra.Util; - -public static class PointerExtensions -{ - public static unsafe ref TField GetField(this ref TPointer reference, int offset) - where TPointer : unmanaged - where TField : unmanaged - { - var pointer = (byte*)Unsafe.AsPointer(ref reference) + offset; - return ref *(TField*)pointer; - } - - public static unsafe ref TField GetField(TPointer* itemPointer, int offset) - where TPointer : unmanaged - where TField : unmanaged - { - var pointer = (byte*)itemPointer + offset; - return ref *(TField*)pointer; - } -}