Scaffolded resource manager
This commit is contained in:
6
SlaveEngine.Assets/Asset.cs
Normal file
6
SlaveEngine.Assets/Asset.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace SlaveEngine.Assets;
|
||||
|
||||
public abstract class Asset {
|
||||
public Guid Guid { get; private set; }
|
||||
public AssetFileInfo FileInfo { get; private set; }
|
||||
}
|
||||
5
SlaveEngine.Assets/AssetClasses/VectorAsset.cs
Normal file
5
SlaveEngine.Assets/AssetClasses/VectorAsset.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace SlaveEngine.Assets;
|
||||
|
||||
public class VectorAsset : Asset {
|
||||
//TODO: insert the data from an SVG here.
|
||||
}
|
||||
6
SlaveEngine.Assets/AssetFileInfo.cs
Normal file
6
SlaveEngine.Assets/AssetFileInfo.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace SlaveEngine.Assets;
|
||||
|
||||
public struct AssetFileInfo {
|
||||
public string Filename;
|
||||
public string FilePath;
|
||||
}
|
||||
5
SlaveEngine.Assets/AssetProcessor.cs
Normal file
5
SlaveEngine.Assets/AssetProcessor.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace SlaveEngine.Assets;
|
||||
|
||||
public abstract class AssetProcessor {
|
||||
public abstract Asset Process(AssetFileInfo fileInfo , byte[] data);
|
||||
}
|
||||
46
SlaveEngine.Assets/AssetProcessorAttribute.cs
Normal file
46
SlaveEngine.Assets/AssetProcessorAttribute.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
namespace SlaveEngine.Assets;
|
||||
|
||||
public class AssetProcessorAttribute : Attribute {
|
||||
public Type ManagedType { get; init; }
|
||||
public string[] ManagedExtensions { get; init; }
|
||||
|
||||
public AssetProcessorAttribute(Type managedType, string[] managedExtensions)
|
||||
{
|
||||
ManagedType = managedType ?? throw new ArgumentNullException(nameof(managedType));
|
||||
|
||||
// Validate that the provided type is Asset or a subclass of Asset
|
||||
if (!typeof(Asset).IsSubclassOf(managedType))
|
||||
throw new ArgumentException($"The type '{managedType.FullName}' must derive from '{typeof(Asset).FullName}'.", nameof(managedType));
|
||||
|
||||
// Validate extensions: non-null, non-empty, normalized, start with '.', no whitespace, unique
|
||||
if (managedExtensions == null)
|
||||
throw new ArgumentNullException(nameof(managedExtensions));
|
||||
|
||||
if (managedExtensions.Length == 0)
|
||||
throw new ArgumentException("At least one file extension must be provided.", nameof(managedExtensions));
|
||||
|
||||
var normalized = new List<string>(managedExtensions.Length);
|
||||
foreach (var ext in managedExtensions)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ext))
|
||||
throw new ArgumentException("Extensions must be non-empty strings.", nameof(managedExtensions));
|
||||
|
||||
var e = ext.Trim();
|
||||
|
||||
// allow either "png" or ".png" as input, normalize to lowercase with leading dot
|
||||
if (!e.StartsWith('.'))
|
||||
e = "." + e;
|
||||
|
||||
if (e.Length < 2)
|
||||
throw new ArgumentException($"Invalid extension '{ext}'.", nameof(managedExtensions));
|
||||
|
||||
if (e.Any(char.IsWhiteSpace))
|
||||
throw new ArgumentException($"Extension '{ext}' contains whitespace.", nameof(managedExtensions));
|
||||
|
||||
normalized.Add(e.ToLowerInvariant());
|
||||
}
|
||||
|
||||
// ensure uniqueness
|
||||
ManagedExtensions = normalized.Distinct().ToArray();
|
||||
}
|
||||
}
|
||||
10
SlaveEngine.Assets/Processors/SVGProcessor.cs
Normal file
10
SlaveEngine.Assets/Processors/SVGProcessor.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace SlaveEngine.Assets.Processors;
|
||||
|
||||
[AssetProcessor(typeof(VectorAsset), managedExtensions: [".svg"])]
|
||||
public sealed class SVGProcessor : AssetProcessor{
|
||||
|
||||
public override Asset Process(AssetFileInfo fileInfo, byte[] data)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
61
SlaveEngine.Assets/ResourceManager.cs
Normal file
61
SlaveEngine.Assets/ResourceManager.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace SlaveEngine.Assets;
|
||||
|
||||
public sealed class ResourceManager {
|
||||
|
||||
public ResourceManager(string basePath)
|
||||
{
|
||||
if (!Path.Exists(basePath))
|
||||
throw new DirectoryNotFoundException($"The base path '{basePath}' does not exist.");
|
||||
BasePath = basePath;
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
private AssetProcessor? GetProcessorForExtension(string extension) =>
|
||||
_assetProcessors.FirstOrDefault(p => (bool)p.GetType()
|
||||
.GetCustomAttribute<AssetProcessorAttribute>()?.ManagedExtensions
|
||||
.Contains(extension)
|
||||
);
|
||||
|
||||
public static ResourceManager? Instance { get; private set; }
|
||||
|
||||
private List<AssetProcessor> _assetProcessors = new();
|
||||
|
||||
public string BasePath { get; private set; }
|
||||
|
||||
public void Initialize(string basePath)
|
||||
{
|
||||
ScanDirectory(basePath);
|
||||
}
|
||||
|
||||
private void ScanDirectory(string directory)
|
||||
{
|
||||
var files = Directory.GetFiles(directory);
|
||||
foreach (var file in files)
|
||||
{
|
||||
var ext = Path.GetExtension(file);
|
||||
// check if we can process this extension
|
||||
var processor = GetProcessorForExtension(ext);
|
||||
if (processor == null) continue;
|
||||
|
||||
var name = Path.GetFileNameWithoutExtension(file);
|
||||
AssetFileInfo fileInfo = new()
|
||||
{
|
||||
Filename = name,
|
||||
FilePath = file
|
||||
};
|
||||
|
||||
processor.Process(fileInfo, File.ReadAllBytes(file));
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterProcessor(AssetProcessor processor)
|
||||
{
|
||||
//make sure the processor for this type isn't already in:
|
||||
var ap = _assetProcessors.FirstOrDefault(p => p.GetType() == processor.GetType());
|
||||
if (ap == null) _assetProcessors.Add(processor);
|
||||
}
|
||||
}
|
||||
9
SlaveEngine.Assets/SlaveEngine.Assets.csproj
Normal file
9
SlaveEngine.Assets/SlaveEngine.Assets.csproj
Normal file
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user