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>
|
||||||
14
Solution.sln
14
Solution.sln
@@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "2DGAMELIB", "2DGAMELIB\2DGA
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlaveMatrix.Extract", "SlaveMatrix.Extract\SlaveMatrix.Extract.csproj", "{DAEAB23A-E099-432E-9C85-39D0853BD78A}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlaveMatrix.Extract", "SlaveMatrix.Extract\SlaveMatrix.Extract.csproj", "{DAEAB23A-E099-432E-9C85-39D0853BD78A}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlaveEngine.Assets", "SlaveEngine.Assets\SlaveEngine.Assets.csproj", "{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x64 = Debug|x64
|
Debug|x64 = Debug|x64
|
||||||
@@ -55,6 +57,18 @@ Global
|
|||||||
{DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|x86.Build.0 = Release|Any CPU
|
{DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|Any CPU.Build.0 = Release|Any CPU
|
{DAEAB23A-E099-432E-9C85-39D0853BD78A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{11B3B1C3-B393-4A4D-AEC5-B26C86F6C0C6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
Reference in New Issue
Block a user