2020-04-22 00:30:55 +02:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.IO;
|
|
|
|
using Microsoft.Xna.Framework;
|
|
|
|
using Microsoft.Xna.Framework.Content;
|
|
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
|
|
|
2020-09-16 23:39:01 +02:00
|
|
|
namespace MLEM.Data.Content {
|
2020-05-20 23:59:40 +02:00
|
|
|
/// <summary>
|
|
|
|
/// Represents a version of <see cref="ContentManager"/> that doesn't load content binary <c>xnb</c> files, but rather as their regular formats.
|
|
|
|
/// </summary>
|
2020-04-22 00:30:55 +02:00
|
|
|
public class RawContentManager : ContentManager, IGameComponent {
|
|
|
|
|
2021-07-27 16:40:42 +02:00
|
|
|
private static List<RawContentReader> readers;
|
2020-04-22 00:30:55 +02:00
|
|
|
|
|
|
|
private readonly List<IDisposable> disposableAssets = new List<IDisposable>();
|
|
|
|
|
2020-05-20 23:59:40 +02:00
|
|
|
/// <summary>
|
|
|
|
/// The graphics device that this content manager uses
|
|
|
|
/// </summary>
|
2020-04-22 00:30:55 +02:00
|
|
|
public readonly GraphicsDevice GraphicsDevice;
|
|
|
|
|
2020-05-20 23:59:40 +02:00
|
|
|
/// <summary>
|
|
|
|
/// Creates a new content manager with an optionally specified root directory.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="serviceProvider">The service provider of your game</param>
|
|
|
|
/// <param name="rootDirectory">The root directory. Defaults to "Content"</param>
|
2020-04-22 00:30:55 +02:00
|
|
|
public RawContentManager(IServiceProvider serviceProvider, string rootDirectory = "Content") :
|
|
|
|
base(serviceProvider, rootDirectory) {
|
|
|
|
if (serviceProvider.GetService(typeof(IGraphicsDeviceService)) is IGraphicsDeviceService s)
|
|
|
|
this.GraphicsDevice = s.GraphicsDevice;
|
|
|
|
}
|
|
|
|
|
2020-05-20 23:59:40 +02:00
|
|
|
/// <summary>
|
|
|
|
/// Loads a raw asset with the given name, based on the <see cref="ContentManager.RootDirectory"/>.
|
|
|
|
/// If the asset was previously loaded using this method, the cached asset is merely returned.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="assetName">The path and name of the asset to load, without extension.</param>
|
|
|
|
/// <typeparam name="T">The type of asset to load</typeparam>
|
|
|
|
/// <returns>The asset, either loaded from the cache, or from disk.</returns>
|
2020-04-22 00:30:55 +02:00
|
|
|
public override T Load<T>(string assetName) {
|
|
|
|
if (this.LoadedAssets.TryGetValue(assetName, out var ret) && ret is T t)
|
|
|
|
return t;
|
|
|
|
return this.Read<T>(assetName, default);
|
|
|
|
}
|
|
|
|
|
2021-11-22 19:25:18 +01:00
|
|
|
/// <summary>
|
|
|
|
/// Reloads the asset of the given type, with the given original name.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="originalAssetName">The original name of the asset.</param>
|
|
|
|
/// <param name="currentAsset">The current asset instance.</param>
|
|
|
|
/// <typeparam name="T">The asset's type.</typeparam>
|
2020-04-22 00:30:55 +02:00
|
|
|
protected override void ReloadAsset<T>(string originalAssetName, T currentAsset) {
|
|
|
|
this.Read(originalAssetName, currentAsset);
|
|
|
|
}
|
|
|
|
|
|
|
|
private T Read<T>(string assetName, T existing) {
|
2020-09-17 01:59:08 +02:00
|
|
|
var triedFiles = new List<string>();
|
2021-07-27 16:40:42 +02:00
|
|
|
if (readers == null)
|
|
|
|
readers = CollectContentReaders();
|
|
|
|
foreach (var reader in readers) {
|
|
|
|
if (!reader.CanRead(typeof(T)))
|
|
|
|
continue;
|
2020-09-17 01:59:08 +02:00
|
|
|
foreach (var ext in reader.GetFileExtensions()) {
|
|
|
|
var file = Path.Combine(this.RootDirectory, $"{assetName}.{ext}");
|
|
|
|
triedFiles.Add(file);
|
2021-10-16 20:22:06 +02:00
|
|
|
try {
|
|
|
|
using (var stream = Path.IsPathRooted(file) ? File.OpenRead(file) : TitleContainer.OpenStream(file)) {
|
|
|
|
var read = reader.Read(this, assetName, stream, typeof(T), existing);
|
|
|
|
if (!(read is T t))
|
|
|
|
throw new ContentLoadException($"{reader} returned non-{typeof(T)} for asset {assetName}");
|
|
|
|
this.LoadedAssets[assetName] = t;
|
|
|
|
if (t is IDisposable d && !this.disposableAssets.Contains(d))
|
|
|
|
this.disposableAssets.Add(d);
|
2021-11-08 23:46:59 +01:00
|
|
|
if (t is GraphicsResource r)
|
|
|
|
r.Name = assetName;
|
2021-10-16 20:22:06 +02:00
|
|
|
return t;
|
|
|
|
}
|
2021-12-28 14:56:11 +01:00
|
|
|
} catch (FileNotFoundException) {}
|
2020-04-22 00:30:55 +02:00
|
|
|
}
|
|
|
|
}
|
2020-09-17 01:59:08 +02:00
|
|
|
throw new ContentLoadException($"Asset {assetName} not found. Tried files {string.Join(", ", triedFiles)}");
|
2020-04-22 00:30:55 +02:00
|
|
|
}
|
|
|
|
|
2021-11-22 19:25:18 +01:00
|
|
|
/// <summary>
|
|
|
|
/// Unloads this content manager, disposing all of the assets that it loaded.
|
|
|
|
/// </summary>
|
2020-04-22 00:30:55 +02:00
|
|
|
public override void Unload() {
|
|
|
|
foreach (var d in this.disposableAssets)
|
|
|
|
d.Dispose();
|
|
|
|
this.disposableAssets.Clear();
|
|
|
|
base.Unload();
|
|
|
|
}
|
|
|
|
|
2021-11-22 19:25:18 +01:00
|
|
|
/// <summary>
|
|
|
|
/// Initializes the component. Used to load non-graphical resources.
|
|
|
|
/// </summary>
|
2021-12-28 14:56:11 +01:00
|
|
|
public void Initialize() {}
|
2020-04-22 00:30:55 +02:00
|
|
|
|
2021-07-27 16:40:42 +02:00
|
|
|
private static List<RawContentReader> CollectContentReaders() {
|
|
|
|
var ret = new List<RawContentReader>();
|
2022-01-02 22:49:39 +01:00
|
|
|
var assemblyExceptions = new List<Exception>();
|
2021-07-27 16:40:42 +02:00
|
|
|
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) {
|
|
|
|
try {
|
|
|
|
if (assembly.IsDynamic)
|
|
|
|
continue;
|
|
|
|
foreach (var type in assembly.GetExportedTypes()) {
|
|
|
|
try {
|
|
|
|
if (type.IsAbstract)
|
|
|
|
continue;
|
|
|
|
if (!type.IsSubclassOf(typeof(RawContentReader)))
|
|
|
|
continue;
|
2021-10-28 22:14:43 +02:00
|
|
|
ret.Add((RawContentReader) Activator.CreateInstance(type));
|
2021-07-27 16:40:42 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
throw new NotSupportedException($"The type {type} cannot be constructed by a RawContentManager. Does it have a visible parameterless constructor?", e);
|
|
|
|
}
|
|
|
|
}
|
2022-01-02 22:49:39 +01:00
|
|
|
} catch (Exception e) {
|
|
|
|
assemblyExceptions.Add(e);
|
2021-07-27 16:40:42 +02:00
|
|
|
}
|
|
|
|
}
|
2022-01-02 22:49:39 +01:00
|
|
|
if (ret.Count <= 0)
|
|
|
|
throw new AggregateException("Failed to construct any RawContentReader instances", assemblyExceptions);
|
2021-07-27 16:40:42 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-04-22 00:30:55 +02:00
|
|
|
}
|
|
|
|
}
|