mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-15 05:04:15 +01:00
Add ITextureProvider.GetFromManifestResource(Assembly,string)
This commit is contained in:
parent
b52d4724e9
commit
cc756c243c
5 changed files with 327 additions and 35 deletions
|
|
@ -0,0 +1,64 @@
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
|
namespace Dalamud.Interface.Internal.SharedImmediateTextures;
|
||||||
|
|
||||||
|
/// <summary>Represents a sharable texture, based on a manifest texture obtained from
|
||||||
|
/// <see cref="Assembly.GetManifestResourceStream(string)"/>.</summary>
|
||||||
|
internal sealed class ManifestResourceSharedImmediateTexture : SharedImmediateTexture
|
||||||
|
{
|
||||||
|
private readonly Assembly assembly;
|
||||||
|
private readonly string name;
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="ManifestResourceSharedImmediateTexture"/> class.</summary>
|
||||||
|
/// <param name="assembly">The assembly containing manifest resources.</param>
|
||||||
|
/// <param name="name">The case-sensitive name of the manifest resource being requested.</param>
|
||||||
|
private ManifestResourceSharedImmediateTexture(Assembly assembly, string name)
|
||||||
|
{
|
||||||
|
this.assembly = assembly;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string SourcePathForDebug => $"{this.assembly.GetName().FullName}:{this.name}";
|
||||||
|
|
||||||
|
/// <summary>Creates a new placeholder instance of <see cref="ManifestResourceSharedImmediateTexture"/>.</summary>
|
||||||
|
/// <param name="args">The arguments to pass to the constructor.</param>
|
||||||
|
/// <returns>The new instance.</returns>
|
||||||
|
public static SharedImmediateTexture CreatePlaceholder((Assembly Assembly, string Name) args) =>
|
||||||
|
new ManifestResourceSharedImmediateTexture(args.Assembly, args.Name);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString() =>
|
||||||
|
$"{nameof(ManifestResourceSharedImmediateTexture)}#{this.InstanceIdForDebug}({this.SourcePathForDebug})";
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void ReleaseResources()
|
||||||
|
{
|
||||||
|
_ = this.UnderlyingWrap?.ToContentDisposedTask(true);
|
||||||
|
this.UnderlyingWrap = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void ReviveResources() =>
|
||||||
|
this.UnderlyingWrap = Service<TextureLoadThrottler>.Get().CreateLoader(
|
||||||
|
this,
|
||||||
|
this.CreateTextureAsync,
|
||||||
|
this.LoadCancellationToken);
|
||||||
|
|
||||||
|
private async Task<IDalamudTextureWrap> CreateTextureAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await using var stream = this.assembly.GetManifestResourceStream(this.name);
|
||||||
|
if (stream is null)
|
||||||
|
throw new FileNotFoundException("The resource file could not be found.");
|
||||||
|
|
||||||
|
var tm = await Service<TextureManager>.GetAsync();
|
||||||
|
var ms = new MemoryStream(stream.CanSeek ? (int)stream.Length : 0);
|
||||||
|
await stream.CopyToAsync(ms, cancellationToken);
|
||||||
|
return tm.NoThrottleGetFromImage(ms.GetBuffer().AsMemory(0, (int)ms.Length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -17,6 +18,7 @@ using Dalamud.Logging.Internal;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
|
||||||
|
using Lumina.Data;
|
||||||
using Lumina.Data.Files;
|
using Lumina.Data.Files;
|
||||||
|
|
||||||
using SharpDX;
|
using SharpDX;
|
||||||
|
|
@ -59,8 +61,14 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
||||||
private readonly TextureLoadThrottler textureLoadThrottler = Service<TextureLoadThrottler>.Get();
|
private readonly TextureLoadThrottler textureLoadThrottler = Service<TextureLoadThrottler>.Get();
|
||||||
|
|
||||||
private readonly ConcurrentLru<GameIconLookup, string> lookupToPath = new(PathLookupLruCount);
|
private readonly ConcurrentLru<GameIconLookup, string> lookupToPath = new(PathLookupLruCount);
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, SharedImmediateTexture> gamePathTextures = new();
|
private readonly ConcurrentDictionary<string, SharedImmediateTexture> gamePathTextures = new();
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, SharedImmediateTexture> fileSystemTextures = new();
|
private readonly ConcurrentDictionary<string, SharedImmediateTexture> fileSystemTextures = new();
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<(Assembly Assembly, string Name), SharedImmediateTexture>
|
||||||
|
manifestResourceTextures = new();
|
||||||
|
|
||||||
private readonly HashSet<SharedImmediateTexture> invalidatedTextures = new();
|
private readonly HashSet<SharedImmediateTexture> invalidatedTextures = new();
|
||||||
|
|
||||||
private bool disposing;
|
private bool disposing;
|
||||||
|
|
@ -71,12 +79,15 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public event ITextureSubstitutionProvider.TextureDataInterceptorDelegate? InterceptTexDataLoad;
|
public event ITextureSubstitutionProvider.TextureDataInterceptorDelegate? InterceptTexDataLoad;
|
||||||
|
|
||||||
/// <summary>Gets all the loaded textures from the game resources.</summary>
|
/// <summary>Gets all the loaded textures from game resources.</summary>
|
||||||
public ICollection<SharedImmediateTexture> GamePathTexturesForDebug => this.gamePathTextures.Values;
|
public ICollection<SharedImmediateTexture> GamePathTexturesForDebug => this.gamePathTextures.Values;
|
||||||
|
|
||||||
/// <summary>Gets all the loaded textures from the game resources.</summary>
|
/// <summary>Gets all the loaded textures from filesystem.</summary>
|
||||||
public ICollection<SharedImmediateTexture> FileSystemTexturesForDebug => this.fileSystemTextures.Values;
|
public ICollection<SharedImmediateTexture> FileSystemTexturesForDebug => this.fileSystemTextures.Values;
|
||||||
|
|
||||||
|
/// <summary>Gets all the loaded textures from assembly manifest resources.</summary>
|
||||||
|
public ICollection<SharedImmediateTexture> ManifestResourceTexturesForDebug => this.manifestResourceTextures.Values;
|
||||||
|
|
||||||
/// <summary>Gets all the loaded textures that are invalidated from <see cref="InvalidatePaths"/>.</summary>
|
/// <summary>Gets all the loaded textures that are invalidated from <see cref="InvalidatePaths"/>.</summary>
|
||||||
/// <remarks><c>lock</c> on use of the value returned from this property.</remarks>
|
/// <remarks><c>lock</c> on use of the value returned from this property.</remarks>
|
||||||
[SuppressMessage(
|
[SuppressMessage(
|
||||||
|
|
@ -92,14 +103,20 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.disposing = true;
|
this.disposing = true;
|
||||||
foreach (var v in this.gamePathTextures.Values)
|
|
||||||
v.ReleaseSelfReference(true);
|
|
||||||
foreach (var v in this.fileSystemTextures.Values)
|
|
||||||
v.ReleaseSelfReference(true);
|
|
||||||
|
|
||||||
|
ReleaseSelfReferences(this.gamePathTextures);
|
||||||
|
ReleaseSelfReferences(this.fileSystemTextures);
|
||||||
|
ReleaseSelfReferences(this.manifestResourceTextures);
|
||||||
this.lookupToPath.Clear();
|
this.lookupToPath.Clear();
|
||||||
this.gamePathTextures.Clear();
|
|
||||||
this.fileSystemTextures.Clear();
|
return;
|
||||||
|
|
||||||
|
static void ReleaseSelfReferences<T>(ConcurrentDictionary<T, SharedImmediateTexture> dict)
|
||||||
|
{
|
||||||
|
foreach (var v in dict.Values)
|
||||||
|
v.ReleaseSelfReference(true);
|
||||||
|
dict.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region API9 compat
|
#region API9 compat
|
||||||
|
|
@ -157,13 +174,29 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
||||||
|
|
||||||
/// <inheritdoc cref="ITextureProvider.GetFromGame"/>
|
/// <inheritdoc cref="ITextureProvider.GetFromGame"/>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public SharedImmediateTexture GetFromGame(string path) =>
|
public SharedImmediateTexture GetFromGame(string path)
|
||||||
this.gamePathTextures.GetOrAdd(path, GamePathSharedImmediateTexture.CreatePlaceholder);
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(this.disposing, this);
|
||||||
|
return this.gamePathTextures.GetOrAdd(path, GamePathSharedImmediateTexture.CreatePlaceholder);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ITextureProvider.GetFromFile"/>
|
/// <inheritdoc cref="ITextureProvider.GetFromFile"/>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public SharedImmediateTexture GetFromFile(string path) =>
|
public SharedImmediateTexture GetFromFile(string path)
|
||||||
this.fileSystemTextures.GetOrAdd(path, FileSystemSharedImmediateTexture.CreatePlaceholder);
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(this.disposing, this);
|
||||||
|
return this.fileSystemTextures.GetOrAdd(path, FileSystemSharedImmediateTexture.CreatePlaceholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ITextureProvider.GetFromFile"/>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public SharedImmediateTexture GetFromManifestResource(Assembly assembly, string name)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(this.disposing, this);
|
||||||
|
return this.manifestResourceTextures.GetOrAdd(
|
||||||
|
(assembly, name),
|
||||||
|
ManifestResourceSharedImmediateTexture.CreatePlaceholder);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|
@ -177,6 +210,11 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
ISharedImmediateTexture ITextureProvider.GetFromFile(string path) => this.GetFromFile(path);
|
ISharedImmediateTexture ITextureProvider.GetFromFile(string path) => this.GetFromFile(path);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
ISharedImmediateTexture ITextureProvider.GetFromManifestResource(Assembly assembly, string name) =>
|
||||||
|
this.GetFromManifestResource(assembly, name);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Task<IDalamudTextureWrap> CreateFromImageAsync(
|
public Task<IDalamudTextureWrap> CreateFromImageAsync(
|
||||||
ReadOnlyMemory<byte> bytes,
|
ReadOnlyMemory<byte> bytes,
|
||||||
|
|
@ -433,15 +471,39 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
||||||
/// <returns>The loaded texture.</returns>
|
/// <returns>The loaded texture.</returns>
|
||||||
internal IDalamudTextureWrap NoThrottleGetFromImage(ReadOnlyMemory<byte> bytes)
|
internal IDalamudTextureWrap NoThrottleGetFromImage(ReadOnlyMemory<byte> bytes)
|
||||||
{
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(this.disposing, this);
|
||||||
|
|
||||||
if (this.interfaceManager.Scene is not { } scene)
|
if (this.interfaceManager.Scene is not { } scene)
|
||||||
{
|
{
|
||||||
_ = Service<InterfaceManager.InterfaceManagerWithScene>.Get();
|
_ = Service<InterfaceManager.InterfaceManagerWithScene>.Get();
|
||||||
scene = this.interfaceManager.Scene ?? throw new InvalidOperationException();
|
scene = this.interfaceManager.Scene ?? throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var bytesArray = bytes.ToArray();
|
||||||
|
var texFileAttemptException = default(Exception);
|
||||||
|
if (TexFileExtensions.IsPossiblyTexFile2D(bytesArray))
|
||||||
|
{
|
||||||
|
var tf = new TexFile();
|
||||||
|
typeof(TexFile).GetProperty(nameof(tf.Data))!.GetSetMethod(true)!.Invoke(
|
||||||
|
tf,
|
||||||
|
new object?[] { bytesArray });
|
||||||
|
typeof(TexFile).GetProperty(nameof(tf.Reader))!.GetSetMethod(true)!.Invoke(
|
||||||
|
tf,
|
||||||
|
new object?[] { new LuminaBinaryReader(bytesArray) });
|
||||||
|
// Note: FileInfo and FilePath are not used from TexFile; skip it.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return this.NoThrottleGetFromTexFile(tf);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
texFileAttemptException = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new DalamudTextureWrap(
|
return new DalamudTextureWrap(
|
||||||
scene.LoadImage(bytes.ToArray())
|
scene.LoadImage(bytesArray)
|
||||||
?? throw new("Failed to load image because of an unknown reason."));
|
?? throw texFileAttemptException ?? new("Failed to load image because of an unknown reason."));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Gets a texture from the given <see cref="TexFile"/>. Skips the load throttler; intended to be used from
|
/// <summary>Gets a texture from the given <see cref="TexFile"/>. Skips the load throttler; intended to be used from
|
||||||
|
|
@ -450,6 +512,8 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
||||||
/// <returns>The loaded texture.</returns>
|
/// <returns>The loaded texture.</returns>
|
||||||
internal IDalamudTextureWrap NoThrottleGetFromTexFile(TexFile file)
|
internal IDalamudTextureWrap NoThrottleGetFromTexFile(TexFile file)
|
||||||
{
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(this.disposing, this);
|
||||||
|
|
||||||
var buffer = file.TextureBuffer;
|
var buffer = file.TextureBuffer;
|
||||||
var (dxgiFormat, conversion) = TexFile.GetDxgiFormatFromTextureFormat(file.Header.Format, false);
|
var (dxgiFormat, conversion) = TexFile.GetDxgiFormatFromTextureFormat(file.Header.Format, false);
|
||||||
if (conversion != TexFile.DxgiFormatConversion.NoConversion || !this.SupportsDxgiFormat(dxgiFormat))
|
if (conversion != TexFile.DxgiFormatConversion.NoConversion || !this.SupportsDxgiFormat(dxgiFormat))
|
||||||
|
|
@ -476,23 +540,9 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
||||||
|
|
||||||
private void FrameworkOnUpdate(IFramework unused)
|
private void FrameworkOnUpdate(IFramework unused)
|
||||||
{
|
{
|
||||||
if (!this.gamePathTextures.IsEmpty)
|
RemoveFinalReleased(this.gamePathTextures);
|
||||||
{
|
RemoveFinalReleased(this.fileSystemTextures);
|
||||||
foreach (var (k, v) in this.gamePathTextures)
|
RemoveFinalReleased(this.manifestResourceTextures);
|
||||||
{
|
|
||||||
if (TextureFinalReleasePredicate(v))
|
|
||||||
_ = this.gamePathTextures.TryRemove(k, out _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.fileSystemTextures.IsEmpty)
|
|
||||||
{
|
|
||||||
foreach (var (k, v) in this.fileSystemTextures)
|
|
||||||
{
|
|
||||||
if (TextureFinalReleasePredicate(v))
|
|
||||||
_ = this.fileSystemTextures.TryRemove(k, out _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReSharper disable once InconsistentlySynchronizedField
|
// ReSharper disable once InconsistentlySynchronizedField
|
||||||
if (this.invalidatedTextures.Count != 0)
|
if (this.invalidatedTextures.Count != 0)
|
||||||
|
|
@ -503,6 +553,20 @@ internal sealed class TextureManager : IServiceType, IDisposable, ITextureProvid
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
static void RemoveFinalReleased<T>(ConcurrentDictionary<T, SharedImmediateTexture> dict)
|
||||||
|
{
|
||||||
|
if (!dict.IsEmpty)
|
||||||
|
{
|
||||||
|
foreach (var (k, v) in dict)
|
||||||
|
{
|
||||||
|
if (TextureFinalReleasePredicate(v))
|
||||||
|
_ = dict.TryRemove(k, out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
static bool TextureFinalReleasePredicate(SharedImmediateTexture v) =>
|
static bool TextureFinalReleasePredicate(SharedImmediateTexture v) =>
|
||||||
v.ContentQueried && v.ReleaseSelfReference(false) == 0 && !v.HasRevivalPossibility;
|
v.ContentQueried && v.ReleaseSelfReference(false) == 0 && !v.HasRevivalPossibility;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Loader;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Dalamud.Interface.Components;
|
using Dalamud.Interface.Components;
|
||||||
|
|
@ -27,6 +30,11 @@ internal class TexWidget : IDataWindowWidget
|
||||||
private bool hq = false;
|
private bool hq = false;
|
||||||
private string inputTexPath = string.Empty;
|
private string inputTexPath = string.Empty;
|
||||||
private string inputFilePath = string.Empty;
|
private string inputFilePath = string.Empty;
|
||||||
|
private Assembly[]? inputManifestResourceAssemblyCandidates;
|
||||||
|
private string[]? inputManifestResourceAssemblyCandidateNames;
|
||||||
|
private int inputManifestResourceAssemblyIndex;
|
||||||
|
private string[]? inputManifestResourceNameCandidates;
|
||||||
|
private int inputManifestResourceNameIndex;
|
||||||
private Vector2 inputTexUv0 = Vector2.Zero;
|
private Vector2 inputTexUv0 = Vector2.Zero;
|
||||||
private Vector2 inputTexUv1 = Vector2.One;
|
private Vector2 inputTexUv1 = Vector2.One;
|
||||||
private Vector4 inputTintCol = Vector4.One;
|
private Vector4 inputTintCol = Vector4.One;
|
||||||
|
|
@ -53,6 +61,11 @@ internal class TexWidget : IDataWindowWidget
|
||||||
this.inputFilePath = Path.Join(
|
this.inputFilePath = Path.Join(
|
||||||
Service<Dalamud>.Get().StartInfo.AssetDirectory!,
|
Service<Dalamud>.Get().StartInfo.AssetDirectory!,
|
||||||
DalamudAsset.Logo.GetAttribute<DalamudAssetPathAttribute>()!.FileName);
|
DalamudAsset.Logo.GetAttribute<DalamudAssetPathAttribute>()!.FileName);
|
||||||
|
this.inputManifestResourceAssemblyCandidates = null;
|
||||||
|
this.inputManifestResourceAssemblyCandidateNames = null;
|
||||||
|
this.inputManifestResourceAssemblyIndex = 0;
|
||||||
|
this.inputManifestResourceNameCandidates = null;
|
||||||
|
this.inputManifestResourceNameIndex = 0;
|
||||||
this.Ready = true;
|
this.Ready = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,19 +78,28 @@ internal class TexWidget : IDataWindowWidget
|
||||||
GC.Collect();
|
GC.Collect();
|
||||||
|
|
||||||
ImGui.PushID("loadedGameTextures");
|
ImGui.PushID("loadedGameTextures");
|
||||||
if (ImGui.CollapsingHeader($"Loaded Game Textures: {this.textureManager.GamePathTexturesForDebug.Count:g}###header"))
|
if (ImGui.CollapsingHeader(
|
||||||
|
$"Loaded Game Textures: {this.textureManager.GamePathTexturesForDebug.Count:g}###header"))
|
||||||
this.DrawLoadedTextures(this.textureManager.GamePathTexturesForDebug);
|
this.DrawLoadedTextures(this.textureManager.GamePathTexturesForDebug);
|
||||||
ImGui.PopID();
|
ImGui.PopID();
|
||||||
|
|
||||||
ImGui.PushID("loadedFileTextures");
|
ImGui.PushID("loadedFileTextures");
|
||||||
if (ImGui.CollapsingHeader($"Loaded File Textures: {this.textureManager.FileSystemTexturesForDebug.Count:g}###header"))
|
if (ImGui.CollapsingHeader(
|
||||||
|
$"Loaded File Textures: {this.textureManager.FileSystemTexturesForDebug.Count:g}###header"))
|
||||||
this.DrawLoadedTextures(this.textureManager.FileSystemTexturesForDebug);
|
this.DrawLoadedTextures(this.textureManager.FileSystemTexturesForDebug);
|
||||||
ImGui.PopID();
|
ImGui.PopID();
|
||||||
|
|
||||||
|
ImGui.PushID("loadedManifestResourceTextures");
|
||||||
|
if (ImGui.CollapsingHeader(
|
||||||
|
$"Loaded Manifest Resource Textures: {this.textureManager.ManifestResourceTexturesForDebug.Count:g}###header"))
|
||||||
|
this.DrawLoadedTextures(this.textureManager.ManifestResourceTexturesForDebug);
|
||||||
|
ImGui.PopID();
|
||||||
|
|
||||||
lock (this.textureManager.InvalidatedTexturesForDebug)
|
lock (this.textureManager.InvalidatedTexturesForDebug)
|
||||||
{
|
{
|
||||||
ImGui.PushID("invalidatedTextures");
|
ImGui.PushID("invalidatedTextures");
|
||||||
if (ImGui.CollapsingHeader($"Invalidated: {this.textureManager.InvalidatedTexturesForDebug.Count:g}###header"))
|
if (ImGui.CollapsingHeader(
|
||||||
|
$"Invalidated: {this.textureManager.InvalidatedTexturesForDebug.Count:g}###header"))
|
||||||
{
|
{
|
||||||
this.DrawLoadedTextures(this.textureManager.InvalidatedTexturesForDebug);
|
this.DrawLoadedTextures(this.textureManager.InvalidatedTexturesForDebug);
|
||||||
}
|
}
|
||||||
|
|
@ -86,16 +108,39 @@ internal class TexWidget : IDataWindowWidget
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.CollapsingHeader("Load Game File by Icon ID", ImGuiTreeNodeFlags.DefaultOpen))
|
if (ImGui.CollapsingHeader("Load Game File by Icon ID", ImGuiTreeNodeFlags.DefaultOpen))
|
||||||
|
{
|
||||||
|
ImGui.PushID(nameof(this.DrawIconInput));
|
||||||
this.DrawIconInput();
|
this.DrawIconInput();
|
||||||
|
ImGui.PopID();
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui.CollapsingHeader("Load Game File by Path", ImGuiTreeNodeFlags.DefaultOpen))
|
if (ImGui.CollapsingHeader("Load Game File by Path", ImGuiTreeNodeFlags.DefaultOpen))
|
||||||
|
{
|
||||||
|
ImGui.PushID(nameof(this.DrawGamePathInput));
|
||||||
this.DrawGamePathInput();
|
this.DrawGamePathInput();
|
||||||
|
ImGui.PopID();
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui.CollapsingHeader("Load File", ImGuiTreeNodeFlags.DefaultOpen))
|
if (ImGui.CollapsingHeader("Load File", ImGuiTreeNodeFlags.DefaultOpen))
|
||||||
|
{
|
||||||
|
ImGui.PushID(nameof(this.DrawFileInput));
|
||||||
this.DrawFileInput();
|
this.DrawFileInput();
|
||||||
|
ImGui.PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.CollapsingHeader("Load Assembly Manifest Resource", ImGuiTreeNodeFlags.DefaultOpen))
|
||||||
|
{
|
||||||
|
ImGui.PushID(nameof(this.DrawAssemblyManifestResourceInput));
|
||||||
|
this.DrawAssemblyManifestResourceInput();
|
||||||
|
ImGui.PopID();
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui.CollapsingHeader("UV"))
|
if (ImGui.CollapsingHeader("UV"))
|
||||||
|
{
|
||||||
|
ImGui.PushID(nameof(this.DrawUvInput));
|
||||||
this.DrawUvInput();
|
this.DrawUvInput();
|
||||||
|
ImGui.PopID();
|
||||||
|
}
|
||||||
|
|
||||||
TextureEntry? toRemove = null;
|
TextureEntry? toRemove = null;
|
||||||
TextureEntry? toCopy = null;
|
TextureEntry? toCopy = null;
|
||||||
|
|
@ -337,6 +382,81 @@ internal class TexWidget : IDataWindowWidget
|
||||||
ImGuiHelpers.ScaledDummy(10);
|
ImGuiHelpers.ScaledDummy(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawAssemblyManifestResourceInput()
|
||||||
|
{
|
||||||
|
if (this.inputManifestResourceAssemblyCandidateNames is null ||
|
||||||
|
this.inputManifestResourceAssemblyCandidates is null)
|
||||||
|
{
|
||||||
|
this.inputManifestResourceAssemblyIndex = 0;
|
||||||
|
this.inputManifestResourceAssemblyCandidates =
|
||||||
|
AssemblyLoadContext
|
||||||
|
.All
|
||||||
|
.SelectMany(x => x.Assemblies)
|
||||||
|
.Distinct()
|
||||||
|
.OrderBy(x => x.GetName().FullName)
|
||||||
|
.ToArray();
|
||||||
|
this.inputManifestResourceAssemblyCandidateNames =
|
||||||
|
this.inputManifestResourceAssemblyCandidates
|
||||||
|
.Select(x => x.GetName().FullName)
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.Combo(
|
||||||
|
"Assembly",
|
||||||
|
ref this.inputManifestResourceAssemblyIndex,
|
||||||
|
this.inputManifestResourceAssemblyCandidateNames,
|
||||||
|
this.inputManifestResourceAssemblyCandidateNames.Length))
|
||||||
|
{
|
||||||
|
this.inputManifestResourceNameIndex = 0;
|
||||||
|
this.inputManifestResourceNameCandidates = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var assembly =
|
||||||
|
this.inputManifestResourceAssemblyIndex >= 0
|
||||||
|
&& this.inputManifestResourceAssemblyIndex < this.inputManifestResourceAssemblyCandidates.Length
|
||||||
|
? this.inputManifestResourceAssemblyCandidates[this.inputManifestResourceAssemblyIndex]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
this.inputManifestResourceNameCandidates ??= assembly?.GetManifestResourceNames() ?? Array.Empty<string>();
|
||||||
|
|
||||||
|
ImGui.Combo(
|
||||||
|
"Name",
|
||||||
|
ref this.inputManifestResourceNameIndex,
|
||||||
|
this.inputManifestResourceNameCandidates,
|
||||||
|
this.inputManifestResourceNameCandidates.Length);
|
||||||
|
|
||||||
|
var name =
|
||||||
|
this.inputManifestResourceNameIndex >= 0
|
||||||
|
&& this.inputManifestResourceNameIndex < this.inputManifestResourceNameCandidates.Length
|
||||||
|
? this.inputManifestResourceNameCandidates[this.inputManifestResourceNameIndex]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (ImGui.Button("Refresh Assemblies"))
|
||||||
|
{
|
||||||
|
this.inputManifestResourceAssemblyIndex = 0;
|
||||||
|
this.inputManifestResourceAssemblyCandidates = null;
|
||||||
|
this.inputManifestResourceAssemblyCandidateNames = null;
|
||||||
|
this.inputManifestResourceNameIndex = 0;
|
||||||
|
this.inputManifestResourceNameCandidates = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assembly is not null && name is not null)
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGui.Button("Load File (Async)"))
|
||||||
|
{
|
||||||
|
this.addedTextures.Add(
|
||||||
|
new(Api10: this.textureManager.GetFromManifestResource(assembly, name).RentAsync()));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGui.Button("Load File (Immediate)"))
|
||||||
|
this.addedTextures.Add(new(Api10ImmManifestResource: (assembly, name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiHelpers.ScaledDummy(10);
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawUvInput()
|
private void DrawUvInput()
|
||||||
{
|
{
|
||||||
ImGui.InputFloat2("UV0", ref this.inputTexUv0);
|
ImGui.InputFloat2("UV0", ref this.inputTexUv0);
|
||||||
|
|
@ -389,7 +509,8 @@ internal class TexWidget : IDataWindowWidget
|
||||||
Task<IDalamudTextureWrap>? Api10 = null,
|
Task<IDalamudTextureWrap>? Api10 = null,
|
||||||
GameIconLookup? Api10ImmGameIcon = null,
|
GameIconLookup? Api10ImmGameIcon = null,
|
||||||
string? Api10ImmGamePath = null,
|
string? Api10ImmGamePath = null,
|
||||||
string? Api10ImmFile = null) : IDisposable
|
string? Api10ImmFile = null,
|
||||||
|
(Assembly Assembly, string Name)? Api10ImmManifestResource = null) : IDisposable
|
||||||
{
|
{
|
||||||
private static int idCounter;
|
private static int idCounter;
|
||||||
|
|
||||||
|
|
@ -421,6 +542,8 @@ internal class TexWidget : IDataWindowWidget
|
||||||
return "Must not happen";
|
return "Must not happen";
|
||||||
if (this.Api10ImmFile is not null)
|
if (this.Api10ImmFile is not null)
|
||||||
return "Must not happen";
|
return "Must not happen";
|
||||||
|
if (this.Api10ImmManifestResource is not null)
|
||||||
|
return "Must not happen";
|
||||||
return "Not implemented";
|
return "Not implemented";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -438,6 +561,13 @@ internal class TexWidget : IDataWindowWidget
|
||||||
return tp.GetFromGame(this.Api10ImmGamePath).GetWrap();
|
return tp.GetFromGame(this.Api10ImmGamePath).GetWrap();
|
||||||
if (this.Api10ImmFile is not null)
|
if (this.Api10ImmFile is not null)
|
||||||
return tp.GetFromFile(this.Api10ImmFile).GetWrap();
|
return tp.GetFromFile(this.Api10ImmFile).GetWrap();
|
||||||
|
if (this.Api10ImmManifestResource is not null)
|
||||||
|
{
|
||||||
|
return tp.GetFromManifestResource(
|
||||||
|
this.Api10ImmManifestResource.Value.Assembly,
|
||||||
|
this.Api10ImmManifestResource.Value.Name).GetWrap();
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -460,6 +590,8 @@ internal class TexWidget : IDataWindowWidget
|
||||||
return $"{nameof(this.Api10ImmGamePath)}: {this.Api10ImmGamePath}";
|
return $"{nameof(this.Api10ImmGamePath)}: {this.Api10ImmGamePath}";
|
||||||
if (this.Api10ImmFile is not null)
|
if (this.Api10ImmFile is not null)
|
||||||
return $"{nameof(this.Api10ImmFile)}: {this.Api10ImmFile}";
|
return $"{nameof(this.Api10ImmFile)}: {this.Api10ImmFile}";
|
||||||
|
if (this.Api10ImmManifestResource is not null)
|
||||||
|
return $"{nameof(this.Api10ImmManifestResource)}: {this.Api10ImmManifestResource}";
|
||||||
return "Not implemented";
|
return "Not implemented";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
@ -41,6 +42,12 @@ public partial interface ITextureProvider
|
||||||
/// <returns>The shared texture that you may use to obtain the loaded texture wrap and load states.</returns>
|
/// <returns>The shared texture that you may use to obtain the loaded texture wrap and load states.</returns>
|
||||||
ISharedImmediateTexture GetFromFile(string path);
|
ISharedImmediateTexture GetFromFile(string path);
|
||||||
|
|
||||||
|
/// <summary>Gets a shared texture corresponding to the given file of the assembly manifest resources.</summary>
|
||||||
|
/// <param name="assembly">The assembly containing manifest resources.</param>
|
||||||
|
/// <param name="name">The case-sensitive name of the manifest resource being requested.</param>
|
||||||
|
/// <returns>The shared texture that you may use to obtain the loaded texture wrap and load states.</returns>
|
||||||
|
ISharedImmediateTexture GetFromManifestResource(Assembly assembly, string name);
|
||||||
|
|
||||||
/// <summary>Gets a texture from the given bytes, trying to interpret it as a .tex file or other well-known image
|
/// <summary>Gets a texture from the given bytes, trying to interpret it as a .tex file or other well-known image
|
||||||
/// files, such as .png.</summary>
|
/// files, such as .png.</summary>
|
||||||
/// <param name="bytes">The bytes to load.</param>
|
/// <param name="bytes">The bytes to load.</param>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
using Dalamud.Memory;
|
||||||
|
|
||||||
using ImGuiScene;
|
using ImGuiScene;
|
||||||
using Lumina.Data.Files;
|
using Lumina.Data.Files;
|
||||||
|
|
||||||
|
|
@ -28,4 +32,25 @@ public static class TexFileExtensions
|
||||||
|
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines if the given data is possibly a <see cref="TexFile"/>.</summary>
|
||||||
|
/// <param name="data">The data.</param>
|
||||||
|
/// <returns><c>true</c> if it should be attempted to be interpreted as a <see cref="TexFile"/>.</returns>
|
||||||
|
internal static unsafe bool IsPossiblyTexFile2D(ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
if (data.Length < Unsafe.SizeOf<TexFile.TexHeader>())
|
||||||
|
return false;
|
||||||
|
fixed (byte* ptr = data)
|
||||||
|
{
|
||||||
|
ref readonly var texHeader = ref MemoryHelper.Cast<TexFile.TexHeader>((nint)ptr);
|
||||||
|
if ((texHeader.Type & TexFile.Attribute.TextureTypeMask) != TexFile.Attribute.TextureType2D)
|
||||||
|
return false;
|
||||||
|
if (!Enum.IsDefined(texHeader.Format))
|
||||||
|
return false;
|
||||||
|
if (texHeader.Width == 0 || texHeader.Height == 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue