mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-02-15 20:37:42 +01:00
Add DalamudAssetManager
This commit is contained in:
parent
01153a2480
commit
a72f407357
17 changed files with 867 additions and 179 deletions
36
Dalamud/Storage/Assets/DalamudAssetAttribute.cs
Normal file
36
Dalamud/Storage/Assets/DalamudAssetAttribute.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
namespace Dalamud.Storage.Assets;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the basic information of a Dalamud asset.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
internal class DalamudAssetAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DalamudAssetAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="purpose">The purpose.</param>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="required">Whether the asset is required.</param>
|
||||
public DalamudAssetAttribute(DalamudAssetPurpose purpose, byte[]? data = null, bool required = true)
|
||||
{
|
||||
this.Purpose = purpose;
|
||||
this.Data = data;
|
||||
this.Required = required;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the purpose of the asset.
|
||||
/// </summary>
|
||||
public DalamudAssetPurpose Purpose { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data, if available.
|
||||
/// </summary>
|
||||
public byte[]? Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the asset is required.
|
||||
/// </summary>
|
||||
public bool Required { get; }
|
||||
}
|
||||
17
Dalamud/Storage/Assets/DalamudAssetExtensions.cs
Normal file
17
Dalamud/Storage/Assets/DalamudAssetExtensions.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using Dalamud.Utility;
|
||||
|
||||
namespace Dalamud.Storage.Assets;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="DalamudAsset"/>.
|
||||
/// </summary>
|
||||
public static class DalamudAssetExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the purpose.
|
||||
/// </summary>
|
||||
/// <param name="asset">The asset.</param>
|
||||
/// <returns>The purpose.</returns>
|
||||
public static DalamudAssetPurpose GetPurpose(this DalamudAsset asset) =>
|
||||
asset.GetAttribute<DalamudAssetAttribute>()?.Purpose ?? DalamudAssetPurpose.Empty;
|
||||
}
|
||||
365
Dalamud/Storage/Assets/DalamudAssetManager.cs
Normal file
365
Dalamud/Storage/Assets/DalamudAssetManager.cs
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.IoC.Internal;
|
||||
using Dalamud.Networking.Http;
|
||||
using Dalamud.Utility;
|
||||
using Dalamud.Utility.Timing;
|
||||
|
||||
using JetBrains.Annotations;
|
||||
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Storage.Assets;
|
||||
|
||||
/// <summary>
|
||||
/// A concrete class for <see cref="IDalamudAssetManager"/>.
|
||||
/// </summary>
|
||||
[PluginInterface]
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
#pragma warning disable SA1015
|
||||
[ResolveVia<IDalamudAssetManager>]
|
||||
#pragma warning restore SA1015
|
||||
internal sealed class DalamudAssetManager : IServiceType, IDisposable, IDalamudAssetManager
|
||||
{
|
||||
private const int DownloadAttemptCount = 10;
|
||||
private const int RenameAttemptCount = 10;
|
||||
|
||||
private readonly object syncRoot = new();
|
||||
private readonly DisposeSafety.ScopedFinalizer scopedFinalizer = new();
|
||||
private readonly Dictionary<DalamudAsset, Task<FileStream>?> fileStreams;
|
||||
private readonly Dictionary<DalamudAsset, Task<IDalamudTextureWrap>?> textureWraps;
|
||||
private readonly Dalamud dalamud;
|
||||
private readonly HappyHttpClient httpClient;
|
||||
private readonly string localSourceDirectory;
|
||||
private readonly CancellationTokenSource cancellationTokenSource;
|
||||
|
||||
private bool isDisposed;
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private DalamudAssetManager(Dalamud dalamud, HappyHttpClient httpClient)
|
||||
{
|
||||
this.dalamud = dalamud;
|
||||
this.httpClient = httpClient;
|
||||
this.localSourceDirectory = Path.Combine(this.dalamud.AssetDirectory.FullName, "..", "local");
|
||||
Directory.CreateDirectory(this.localSourceDirectory);
|
||||
this.scopedFinalizer.Add(this.cancellationTokenSource = new());
|
||||
|
||||
this.fileStreams = Enum.GetValues<DalamudAsset>().ToDictionary(x => x, _ => (Task<FileStream>?)null);
|
||||
this.textureWraps = Enum.GetValues<DalamudAsset>().ToDictionary(x => x, _ => (Task<IDalamudTextureWrap>?)null);
|
||||
|
||||
var loadTimings = Timings.Start("DAM LoadAll");
|
||||
this.WaitForAllRequiredAssets().ContinueWith(_ => loadTimings.Dispose());
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDalamudTextureWrap Empty4X4 => this.GetDalamudTextureWrap(DalamudAsset.Empty4X4);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
lock (this.syncRoot)
|
||||
{
|
||||
if (this.isDisposed)
|
||||
return;
|
||||
|
||||
this.isDisposed = true;
|
||||
}
|
||||
|
||||
this.cancellationTokenSource.Cancel();
|
||||
Task.WaitAll(
|
||||
Array.Empty<Task>()
|
||||
.Concat(this.fileStreams.Values)
|
||||
.Concat(this.textureWraps.Values)
|
||||
.Where(x => x is not null)
|
||||
.ToArray());
|
||||
this.scopedFinalizer.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for all the required assets to be ready. Will result in a faulted task, if any of the required assets
|
||||
/// has failed to load.
|
||||
/// </summary>
|
||||
/// <returns>The task.</returns>
|
||||
[Pure]
|
||||
public Task WaitForAllRequiredAssets()
|
||||
{
|
||||
lock (this.syncRoot)
|
||||
{
|
||||
return Task.WhenAll(
|
||||
Enum.GetValues<DalamudAsset>()
|
||||
.Where(x => x is not DalamudAsset.Empty4X4)
|
||||
.Select(this.CreateStreamAsync)
|
||||
.Select(x => x.ToContentDisposedTask()));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Pure]
|
||||
public bool IsStreamImmediatelyAvailable(DalamudAsset asset) =>
|
||||
asset.GetAttribute<DalamudAssetAttribute>()?.Data is not null
|
||||
|| this.fileStreams[asset]?.IsCompletedSuccessfully is true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Pure]
|
||||
public Stream CreateStream(DalamudAsset asset)
|
||||
{
|
||||
var s = this.CreateStreamAsync(asset);
|
||||
s.Wait();
|
||||
if (s.IsCompletedSuccessfully)
|
||||
return s.Result;
|
||||
if (s.Exception is not null)
|
||||
throw new AggregateException(s.Exception.InnerExceptions);
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Pure]
|
||||
public Task<Stream> CreateStreamAsync(DalamudAsset asset)
|
||||
{
|
||||
if (asset.GetAttribute<DalamudAssetAttribute>() is { Data: { } rawData })
|
||||
return Task.FromResult<Stream>(new MemoryStream(rawData, false));
|
||||
|
||||
Task<FileStream> task;
|
||||
lock (this.syncRoot)
|
||||
{
|
||||
if (this.isDisposed)
|
||||
throw new ObjectDisposedException(nameof(DalamudAssetManager));
|
||||
|
||||
task = this.fileStreams[asset] ??= CreateInnerAsync();
|
||||
}
|
||||
|
||||
return this.TransformImmediate(
|
||||
task,
|
||||
x => (Stream)new FileStream(
|
||||
x.Name,
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.Read,
|
||||
4096,
|
||||
FileOptions.Asynchronous | FileOptions.SequentialScan));
|
||||
|
||||
async Task<FileStream> CreateInnerAsync()
|
||||
{
|
||||
string path;
|
||||
List<Exception?> exceptions = null;
|
||||
foreach (var name in asset.GetAttributes<DalamudAssetPathAttribute>().Select(x => x.FileName))
|
||||
{
|
||||
if (!File.Exists(path = Path.Combine(this.dalamud.AssetDirectory.FullName, name)))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
return File.OpenRead(path);
|
||||
}
|
||||
catch (Exception e) when (e is not OperationCanceledException)
|
||||
{
|
||||
exceptions ??= new();
|
||||
exceptions.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (File.Exists(path = Path.Combine(this.localSourceDirectory, asset.ToString())))
|
||||
{
|
||||
try
|
||||
{
|
||||
return File.OpenRead(path);
|
||||
}
|
||||
catch (Exception e) when (e is not OperationCanceledException)
|
||||
{
|
||||
exceptions ??= new();
|
||||
exceptions.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
var tempPath = $"{path}.{Environment.ProcessId:x}.{Environment.CurrentManagedThreadId:x}";
|
||||
try
|
||||
{
|
||||
for (var i = 0; i < DownloadAttemptCount; i++)
|
||||
{
|
||||
var attemptedAny = false;
|
||||
foreach (var url in asset.GetAttributes<DalamudAssetOnlineSourceAttribute>())
|
||||
{
|
||||
Log.Information("[{who}] {asset}: Trying {url}", nameof(DalamudAssetManager), asset, url);
|
||||
attemptedAny = true;
|
||||
|
||||
try
|
||||
{
|
||||
await using var tempPathStream = File.Open(tempPath, FileMode.Create, FileAccess.Write);
|
||||
await url.DownloadAsync(
|
||||
this.httpClient.SharedHttpClient,
|
||||
tempPathStream,
|
||||
this.cancellationTokenSource.Token);
|
||||
tempPathStream.Dispose();
|
||||
for (var j = RenameAttemptCount; ; j--)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Move(tempPath, path);
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
if (j == 0)
|
||||
throw;
|
||||
Log.Warning(
|
||||
ioe,
|
||||
"[{who}] {asset}: Renaming failed; trying again {n} more times",
|
||||
nameof(DalamudAssetManager),
|
||||
asset,
|
||||
j);
|
||||
await Task.Delay(1000, this.cancellationTokenSource.Token);
|
||||
continue;
|
||||
}
|
||||
|
||||
return File.OpenRead(path);
|
||||
}
|
||||
}
|
||||
catch (Exception e) when (e is not OperationCanceledException)
|
||||
{
|
||||
Log.Error(e, "[{who}] {asset}: Failed {url}", nameof(DalamudAssetManager), asset, url);
|
||||
}
|
||||
}
|
||||
|
||||
if (!attemptedAny)
|
||||
throw new FileNotFoundException($"Failed to find the asset {asset}.", asset.ToString());
|
||||
|
||||
// Wait up to 5 minutes
|
||||
var delay = Math.Min(300, (1 << i) * 1000);
|
||||
Log.Error(
|
||||
"[{who}] {asset}: Failed to download. Trying again in {sec} seconds...",
|
||||
nameof(DalamudAssetManager),
|
||||
asset,
|
||||
delay);
|
||||
await Task.Delay(delay * 1000, this.cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
throw new FileNotFoundException($"Failed to load the asset {asset}.", asset.ToString());
|
||||
}
|
||||
catch (Exception e) when (e is not OperationCanceledException)
|
||||
{
|
||||
exceptions ??= new();
|
||||
exceptions.Add(e);
|
||||
try
|
||||
{
|
||||
File.Delete(tempPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// don't care
|
||||
}
|
||||
}
|
||||
|
||||
throw new AggregateException(exceptions);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Pure]
|
||||
public IDalamudTextureWrap GetDalamudTextureWrap(DalamudAsset asset) =>
|
||||
ExtractResult(this.GetDalamudTextureWrapAsync(asset));
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Pure]
|
||||
[return: NotNullIfNotNull(nameof(defaultWrap))]
|
||||
public IDalamudTextureWrap? GetDalamudTextureWrap(DalamudAsset asset, IDalamudTextureWrap? defaultWrap)
|
||||
{
|
||||
var task = this.GetDalamudTextureWrapAsync(asset);
|
||||
return task.IsCompletedSuccessfully ? task.Result : defaultWrap;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Pure]
|
||||
public Task<IDalamudTextureWrap> GetDalamudTextureWrapAsync(DalamudAsset asset)
|
||||
{
|
||||
var purpose = asset.GetPurpose();
|
||||
if (purpose is not DalamudAssetPurpose.TextureFromPng and not DalamudAssetPurpose.TextureFromRaw)
|
||||
throw new ArgumentOutOfRangeException(nameof(asset), asset, "The asset cannot be taken as a Texture2D.");
|
||||
|
||||
Task<IDalamudTextureWrap> task;
|
||||
lock (this.syncRoot)
|
||||
{
|
||||
if (this.isDisposed)
|
||||
throw new ObjectDisposedException(nameof(DalamudAssetManager));
|
||||
|
||||
task = this.textureWraps[asset] ??= CreateInnerAsync();
|
||||
}
|
||||
|
||||
return task;
|
||||
|
||||
async Task<IDalamudTextureWrap> CreateInnerAsync()
|
||||
{
|
||||
var buf = Array.Empty<byte>();
|
||||
try
|
||||
{
|
||||
var im = (await Service<InterfaceManager.InterfaceManagerWithScene>.GetAsync()).Manager;
|
||||
await using var stream = await this.CreateStreamAsync(asset);
|
||||
var length = checked((int)stream.Length);
|
||||
buf = ArrayPool<byte>.Shared.Rent(length);
|
||||
stream.ReadExactly(buf, 0, length);
|
||||
var image = purpose switch
|
||||
{
|
||||
DalamudAssetPurpose.TextureFromPng => im.LoadImage(buf),
|
||||
DalamudAssetPurpose.TextureFromRaw =>
|
||||
asset.GetAttribute<DalamudAssetRawTextureAttribute>() is { } raw
|
||||
? im.LoadImageFromDxgiFormat(buf, raw.Pitch, raw.Width, raw.Height, raw.Format)
|
||||
: throw new InvalidOperationException(
|
||||
"TextureFromRaw must accompany a DalamudAssetRawTextureAttribute."),
|
||||
_ => null,
|
||||
};
|
||||
var disposeDeferred =
|
||||
this.scopedFinalizer.Add(image)
|
||||
?? throw new InvalidOperationException("Something went wrong very badly");
|
||||
return new DisposeSuppressingDalamudTextureWrap(disposeDeferred);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "[{name}] Failed to load {asset}.", nameof(DalamudAssetManager), asset);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static T ExtractResult<T>(Task<T> t) => t.IsCompleted ? t.Result : t.GetAwaiter().GetResult();
|
||||
|
||||
private Task<TOut> TransformImmediate<TIn, TOut>(Task<TIn> task, Func<TIn, TOut> transformer)
|
||||
{
|
||||
if (task.IsCompletedSuccessfully)
|
||||
return Task.FromResult(transformer(task.Result));
|
||||
if (task.Exception is { } exc)
|
||||
return Task.FromException<TOut>(exc);
|
||||
return task.ContinueWith(_ => this.TransformImmediate(task, transformer)).Unwrap();
|
||||
}
|
||||
|
||||
private class DisposeSuppressingDalamudTextureWrap : IDalamudTextureWrap
|
||||
{
|
||||
private readonly IDalamudTextureWrap innerWrap;
|
||||
|
||||
public DisposeSuppressingDalamudTextureWrap(IDalamudTextureWrap wrap) => this.innerWrap = wrap;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr ImGuiHandle => this.innerWrap.ImGuiHandle;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Width => this.innerWrap.Width;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Height => this.innerWrap.Height;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
// suppressed
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Dalamud/Storage/Assets/DalamudAssetOnlineSourceAttribute.cs
Normal file
48
Dalamud/Storage/Assets/DalamudAssetOnlineSourceAttribute.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dalamud.Storage.Assets;
|
||||
|
||||
/// <summary>
|
||||
/// Marks that an asset can be download from online.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
|
||||
internal class DalamudAssetOnlineSourceAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DalamudAssetOnlineSourceAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
public DalamudAssetOnlineSourceAttribute(string url)
|
||||
{
|
||||
this.Url = url;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the source URL of the file.
|
||||
/// </summary>
|
||||
public string Url { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Downloads to the given stream.
|
||||
/// </summary>
|
||||
/// <param name="client">The client.</param>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The task.</returns>
|
||||
public async Task DownloadAsync(HttpClient client, Stream stream, CancellationToken cancellationToken)
|
||||
{
|
||||
using var resp = await client.GetAsync(this.Url, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
if (resp.StatusCode != HttpStatusCode.OK)
|
||||
throw new NotSupportedException($"Only 200 OK is supported; got {resp.StatusCode}");
|
||||
|
||||
await using var readStream = await resp.Content.ReadAsStreamAsync(cancellationToken);
|
||||
await readStream.CopyToAsync(stream, cancellationToken);
|
||||
if (resp.Content.Headers.ContentLength is { } length && stream.Length != length)
|
||||
throw new IOException($"Expected {length} bytes; got {stream.Length} bytes.");
|
||||
}
|
||||
}
|
||||
21
Dalamud/Storage/Assets/DalamudAssetPathAttribute.cs
Normal file
21
Dalamud/Storage/Assets/DalamudAssetPathAttribute.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using System.IO;
|
||||
|
||||
namespace Dalamud.Storage.Assets;
|
||||
|
||||
/// <summary>
|
||||
/// File names to look up in Dalamud assets.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
|
||||
internal class DalamudAssetPathAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DalamudAssetPathAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pathComponents">The path components.</param>
|
||||
public DalamudAssetPathAttribute(params string[] pathComponents) => this.FileName = Path.Join(pathComponents);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file name.
|
||||
/// </summary>
|
||||
public string FileName { get; }
|
||||
}
|
||||
27
Dalamud/Storage/Assets/DalamudAssetPurpose.cs
Normal file
27
Dalamud/Storage/Assets/DalamudAssetPurpose.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
namespace Dalamud.Storage.Assets;
|
||||
|
||||
/// <summary>
|
||||
/// Purposes of a Dalamud asset.
|
||||
/// </summary>
|
||||
public enum DalamudAssetPurpose
|
||||
{
|
||||
/// <summary>
|
||||
/// The asset has no purpose.
|
||||
/// </summary>
|
||||
Empty = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The asset is a .png file, and can be purposed as a <see cref="SharpDX.Direct3D11.Texture2D"/>.
|
||||
/// </summary>
|
||||
TextureFromPng = 10,
|
||||
|
||||
/// <summary>
|
||||
/// The asset is a raw texture, and can be purposed as a <see cref="SharpDX.Direct3D11.Texture2D"/>.
|
||||
/// </summary>
|
||||
TextureFromRaw = 1001,
|
||||
|
||||
/// <summary>
|
||||
/// The asset is a font file.
|
||||
/// </summary>
|
||||
Font = 2000,
|
||||
}
|
||||
45
Dalamud/Storage/Assets/DalamudAssetRawTextureAttribute.cs
Normal file
45
Dalamud/Storage/Assets/DalamudAssetRawTextureAttribute.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
using SharpDX.DXGI;
|
||||
|
||||
namespace Dalamud.Storage.Assets;
|
||||
|
||||
/// <summary>
|
||||
/// Provide raw texture data directly.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
internal class DalamudAssetRawTextureAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DalamudAssetRawTextureAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="width">The width.</param>
|
||||
/// <param name="pitch">The pitch.</param>
|
||||
/// <param name="height">The height.</param>
|
||||
/// <param name="format">The format.</param>
|
||||
public DalamudAssetRawTextureAttribute(int width, int pitch, int height, Format format)
|
||||
{
|
||||
this.Width = width;
|
||||
this.Pitch = pitch;
|
||||
this.Height = height;
|
||||
this.Format = format;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width.
|
||||
/// </summary>
|
||||
public int Width { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pitch.
|
||||
/// </summary>
|
||||
public int Pitch { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height.
|
||||
/// </summary>
|
||||
public int Height { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the format.
|
||||
/// </summary>
|
||||
public Format Format { get; }
|
||||
}
|
||||
79
Dalamud/Storage/Assets/IDalamudAssetManager.cs
Normal file
79
Dalamud/Storage/Assets/IDalamudAssetManager.cs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
using System.Diagnostics.Contracts;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Interface.Internal;
|
||||
|
||||
namespace Dalamud.Storage.Assets;
|
||||
|
||||
/// <summary>
|
||||
/// Holds Dalamud Assets' handles hostage, so that they do not get closed while Dalamud is running.<br />
|
||||
/// Also, attempts to load optional assets.<br />
|
||||
/// <br />
|
||||
/// <strong>Note on <see cref="PureAttribute"/></strong><br />
|
||||
/// It will help you get notified if you discard the result of functions, mostly likely because of a mistake.
|
||||
/// Think of C++ [[nodiscard]]. Also, like the intended meaning of the attribute, such methods will not have
|
||||
/// externally visible state changes.
|
||||
/// </summary>
|
||||
internal interface IDalamudAssetManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the shared texture wrap for <see cref="DalamudAsset.Empty4X4"/>.
|
||||
/// </summary>
|
||||
IDalamudTextureWrap Empty4X4 { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the stream for the asset is instantly available.
|
||||
/// </summary>
|
||||
/// <param name="asset">The asset.</param>
|
||||
/// <returns>Whether the stream of an asset is immediately available.</returns>
|
||||
[Pure]
|
||||
bool IsStreamImmediatelyAvailable(DalamudAsset asset);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a stream backed by the specified asset, waiting as necessary.<br />
|
||||
/// <strong>Call <see cref="IDisposable.Dispose"/> after use.</strong>
|
||||
/// </summary>
|
||||
/// <param name="asset">The asset.</param>
|
||||
/// <returns>The stream.</returns>
|
||||
[Pure]
|
||||
Stream CreateStream(DalamudAsset asset);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a stream backed by the specified asset.<br />
|
||||
/// <strong>Call <see cref="IDisposable.Dispose"/> after use.</strong>
|
||||
/// </summary>
|
||||
/// <param name="asset">The asset.</param>
|
||||
/// <returns>The stream, wrapped inside a <see cref="Stream"/>.</returns>
|
||||
[Pure]
|
||||
Task<Stream> CreateStreamAsync(DalamudAsset asset);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a shared instance of <see cref="IDalamudTextureWrap"/>, after waiting as necessary.<br />
|
||||
/// Calls to <see cref="IDisposable.Dispose"/> is unnecessary; they will be ignored.
|
||||
/// </summary>
|
||||
/// <param name="asset">The texture asset.</param>
|
||||
/// <returns>The texture wrap.</returns>
|
||||
[Pure]
|
||||
IDalamudTextureWrap GetDalamudTextureWrap(DalamudAsset asset);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a shared instance of <see cref="IDalamudTextureWrap"/> if it is available instantly;
|
||||
/// if it is not ready, returns <paramref name="defaultWrap"/>.<br />
|
||||
/// Calls to <see cref="IDisposable.Dispose"/> is unnecessary; they will be ignored.
|
||||
/// </summary>
|
||||
/// <param name="asset">The texture asset.</param>
|
||||
/// <param name="defaultWrap">The default return value, if the asset is not ready for whatever reason.</param>
|
||||
/// <returns>The texture wrap.</returns>
|
||||
[Pure]
|
||||
IDalamudTextureWrap? GetDalamudTextureWrap(DalamudAsset asset, IDalamudTextureWrap? defaultWrap);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a shared instance of <see cref="IDalamudTextureWrap"/> in a <see cref="Task{T}"/>.<br />
|
||||
/// Calls to <see cref="IDisposable.Dispose"/> is unnecessary; they will be ignored.
|
||||
/// </summary>
|
||||
/// <param name="asset">The texture asset.</param>
|
||||
/// <returns>The new texture wrap, wrapped inside a <see cref="Task{T}"/>.</returns>
|
||||
[Pure]
|
||||
Task<IDalamudTextureWrap> GetDalamudTextureWrapAsync(DalamudAsset asset);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue