From 887bacaaa49fb55dcdb80dc5bea7e3a5d2f1c35e Mon Sep 17 00:00:00 2001 From: JerryImMouse Date: Thu, 11 Jul 2024 04:03:17 +0500 Subject: [PATCH 1/8] init --- .../Resources/Manager/IResourceManager.cs | 6 + .../Resources/Manager/ResourceManager.cs | 6 + Hypercube.Shared/Resources/ResourcePath.cs | 143 ++++++++++++++++++ .../Hypercube.UnitTests.csproj | 1 + .../ResourceManager/ResourcePathTest.cs | 56 +++++++ 5 files changed, 212 insertions(+) create mode 100644 Hypercube.Shared/Resources/Manager/IResourceManager.cs create mode 100644 Hypercube.Shared/Resources/Manager/ResourceManager.cs create mode 100644 Hypercube.Shared/Resources/ResourcePath.cs create mode 100644 Hypercube.UnitTests/ResourceManager/ResourcePathTest.cs diff --git a/Hypercube.Shared/Resources/Manager/IResourceManager.cs b/Hypercube.Shared/Resources/Manager/IResourceManager.cs new file mode 100644 index 0000000..b5c9d0f --- /dev/null +++ b/Hypercube.Shared/Resources/Manager/IResourceManager.cs @@ -0,0 +1,6 @@ +namespace Hypercube.Shared.Resources.Manager; + +public class IResourceManager +{ + +} \ No newline at end of file diff --git a/Hypercube.Shared/Resources/Manager/ResourceManager.cs b/Hypercube.Shared/Resources/Manager/ResourceManager.cs new file mode 100644 index 0000000..783b1c5 --- /dev/null +++ b/Hypercube.Shared/Resources/Manager/ResourceManager.cs @@ -0,0 +1,6 @@ +namespace Hypercube.Shared.Resources.Manager; + +public class ResourceManager : IResourceManager +{ + +} \ No newline at end of file diff --git a/Hypercube.Shared/Resources/ResourcePath.cs b/Hypercube.Shared/Resources/ResourcePath.cs new file mode 100644 index 0000000..2fd2432 --- /dev/null +++ b/Hypercube.Shared/Resources/ResourcePath.cs @@ -0,0 +1,143 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Hypercube.Shared.Resources; + +public struct ResourcePath +{ + public const char Separator = '/'; + public const string SeparatorStr = "/"; + public const char WinSeparator = '\\'; + public const string WinSeparatorStr = @"\"; + + public static readonly ResourcePath Self = "."; + + + public ResourcePath(string path) + { + if (OperatingSystem.IsWindows()) + { + Path = path.Replace('\\', '/'); + return; + } + + Path = path; + } + + public readonly string Path { get; } + + public bool Rooted => Path.Length > 0 && Path[0] == Separator; + public bool Relative => !Rooted; + public bool IsSelf => Path == Self.Path; + + public string FilenameWithExt + { + get + { + var sepIndex = Path.LastIndexOf('/') + 1; + return sepIndex == -1 ? "" : Path[sepIndex..]; + } + } + + public string Extension + { + get + { + var filename = FilenameWithExt; + + var ind = filename.LastIndexOf('.'); + return ind <= 1 + ? string.Empty + : filename[ind..]; + } + } + + public string Filename + { + get + { + var filename = FilenameWithExt; + + var ind = filename.LastIndexOf('.'); + return ind <= 0 + ? filename + : filename[..ind]; + } + } + + public ResourcePath ParentDirectory + { + get + { + if (IsSelf) + { + return Self; + } + + var idx = Path.Length > 1 && Path[^1] == '/' + ? Path[..^1].LastIndexOf('/') + : Path.LastIndexOf('/'); + + return idx switch + { + -1 => Self, + 0 => new ResourcePath(Path[..1]), + _ => new ResourcePath(Path[..idx]) + }; + } + } + + public static implicit operator ResourcePath(string path) + { + return new ResourcePath(path); + } + + public static ResourcePath operator +(ResourcePath l, ResourcePath r) + { + if (r.IsSelf) + return l; + if (r.Rooted) + return r; + if (l.Path == "") + return new ResourcePath("/" + r.Path); + + if (l.Path.EndsWith("/")) + { + return new ResourcePath(l.Path + r.Path); + } + + return new ResourcePath(l.Path + "/" + r.Path); + } + + public static bool Equals(ResourcePath a, ResourcePath b) + { + return a.Path == b.Path; + } + + public bool Equals(ResourcePath b) + { + return Path == b.Path; + } + + public override bool Equals(object? other) + { + if (other is null || other is not ResourcePath b) + return false; + + return Path == b.Path; + } + + public override int GetHashCode() + { + return Path.GetHashCode(); + } + + public static bool operator ==(ResourcePath a, ResourcePath b) + { + return a.Equals(b); + } + + public static bool operator !=(ResourcePath a, ResourcePath b) + { + return !a.Equals(b); + } +} \ No newline at end of file diff --git a/Hypercube.UnitTests/Hypercube.UnitTests.csproj b/Hypercube.UnitTests/Hypercube.UnitTests.csproj index f2eeddd..d3f807d 100644 --- a/Hypercube.UnitTests/Hypercube.UnitTests.csproj +++ b/Hypercube.UnitTests/Hypercube.UnitTests.csproj @@ -19,6 +19,7 @@ + diff --git a/Hypercube.UnitTests/ResourceManager/ResourcePathTest.cs b/Hypercube.UnitTests/ResourceManager/ResourcePathTest.cs new file mode 100644 index 0000000..8246447 --- /dev/null +++ b/Hypercube.UnitTests/ResourceManager/ResourcePathTest.cs @@ -0,0 +1,56 @@ +using Hypercube.Shared.Resources; + +namespace Hypercube.UnitTests.ResourceManager; + +public class ResourcePathTest +{ + [Test] + public void GetFilenameTest() + { + var resPath = new ResourcePath("/Rooted/path.txt"); + Assert.That(resPath.Filename == "path"); + + Assert.Pass($"ResPath file name is {resPath.Filename}"); + } + + [Test] + public void GetExtensionTest() + { + var resPath = new ResourcePath("/Rooted/path.txt"); + Assert.That(resPath.Extension == ".txt"); + Assert.Pass($"ResPath ext is {resPath.Extension}"); + } + + [Test] + public void RootedTest() + { + var resPath = new ResourcePath("/Rooted/path.txt"); + Assert.That(resPath.Rooted); + Assert.Pass("Res path is rooted"); + } + + [Test] + public void PathConcatTest() + { + var resPath = new ResourcePath("/Rooted/"); + var resPath2 = new ResourcePath("path.txt"); + + var concated = resPath + resPath2; + Assert.That(concated.Path == "/Rooted/path.txt"); + + Assert.Pass($"Concatenated path is equal to {concated.Path}"); + } + + [Test] + public void PathEqualityTest() + { + var resPath = new ResourcePath("/Rooted/"); + var resPath2 = new ResourcePath("/Rooted/"); + var resPath3 = new ResourcePath("path.txt"); + + Assert.That(resPath != resPath3); + Assert.That(resPath == resPath2); + + Assert.Pass("Passed equality test"); + } +} \ No newline at end of file From a3370592b062c26d329de9b2cd6370a1cba83120 Mon Sep 17 00:00:00 2001 From: JerryImMouse Date: Thu, 11 Jul 2024 06:31:41 +0500 Subject: [PATCH 2/8] resource manager improves --- Hypercube.Client/Dependencies.cs | 4 + .../Graphics/Rendering/Renderer.Render.cs | 8 +- .../Graphics/Rendering/Renderer.cs | 4 +- Hypercube.Client/Graphics/Shading/Shader.cs | 28 ++-- .../Graphics/Texturing/ITextureManager.cs | 10 +- .../Graphics/Texturing/TextureManager.cs | 16 ++- .../Manager/GlfwWindowManager.Window.cs | 6 +- .../Windows/Manager/IWindowManager.cs | 4 +- .../Resources/DirRoot/DirContentRoot.cs | 63 ++++++++ Hypercube.Shared/Resources/IContentRoot.cs | 9 ++ .../Resources/Manager/IResourceManager.cs | 14 +- .../Resources/Manager/ResourceManager.cs | 136 +++++++++++++++++- Hypercube.Shared/Resources/ResourcePath.cs | 44 +++++- .../Utilities/Helpers/PathHelpers.cs | 25 ++++ .../ResourceManager/ResourceManagerTests.cs | 22 +++ .../ResourceManager/ResourcePathTest.cs | 8 ++ Resources/Shaders/{ => base}/base.frag | 0 Resources/Shaders/{ => base}/base.vert | 0 18 files changed, 371 insertions(+), 30 deletions(-) create mode 100644 Hypercube.Shared/Resources/DirRoot/DirContentRoot.cs create mode 100644 Hypercube.Shared/Resources/IContentRoot.cs create mode 100644 Hypercube.Shared/Utilities/Helpers/PathHelpers.cs create mode 100644 Hypercube.UnitTests/ResourceManager/ResourceManagerTests.cs rename Resources/Shaders/{ => base}/base.frag (100%) rename Resources/Shaders/{ => base}/base.vert (100%) diff --git a/Hypercube.Client/Dependencies.cs b/Hypercube.Client/Dependencies.cs index 6985f28..2e75cf9 100644 --- a/Hypercube.Client/Dependencies.cs +++ b/Hypercube.Client/Dependencies.cs @@ -9,6 +9,7 @@ using Hypercube.Shared.Entities.Realisation.EventBus; using Hypercube.Shared.Entities.Realisation.Manager; using Hypercube.Shared.EventBus; +using Hypercube.Shared.Resources.Manager; using Hypercube.Shared.Timing; namespace Hypercube.Client; @@ -31,6 +32,9 @@ public static void Register(DependenciesContainer rootContainer) rootContainer.Register(); rootContainer.Register(); + // Resources + rootContainer.Register(); + // Texturing rootContainer.Register(); diff --git a/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs b/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs index 4ba1f23..157f158 100644 --- a/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs +++ b/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs @@ -31,8 +31,12 @@ public sealed partial class Renderer private void OnLoad() { - _baseShader = new Shader("Resources/Shaders/base"); - _baseTexture = _textureManager.CreateHandler("Resources/Textures/icon.png"); + _resourceManager.MountContentFolder("Resources", "/"); + var read = _resourceManager.WrapStream(_resourceManager.ReadFileContent("/Debug/test.txt")!).ReadToEnd(); + Console.WriteLine(read); + Console.WriteLine(read); + _baseShader = new Shader("/Shaders/base/", _resourceManager); + _baseTexture = _textureManager.CreateHandler("/Textures/icon.png"); _baseTexture.Bind(); _vbo = new BufferObject(BufferTarget.ArrayBuffer); diff --git a/Hypercube.Client/Graphics/Rendering/Renderer.cs b/Hypercube.Client/Graphics/Rendering/Renderer.cs index 4d55791..3e58e29 100644 --- a/Hypercube.Client/Graphics/Rendering/Renderer.cs +++ b/Hypercube.Client/Graphics/Rendering/Renderer.cs @@ -7,6 +7,7 @@ using Hypercube.Shared.Dependency; using Hypercube.Shared.EventBus; using Hypercube.Shared.Logging; +using Hypercube.Shared.Resources.Manager; using Hypercube.Shared.Runtimes.Event; using Hypercube.Shared.Runtimes.Loop.Event; using Hypercube.Shared.Timing; @@ -21,6 +22,7 @@ public sealed partial class Renderer : IRenderer, IPostInject [Dependency] private readonly IEventBus _eventBus = default!; [Dependency] private readonly ITextureManager _textureManager = default!; [Dependency] private readonly ITiming _timing = default!; + [Dependency] private readonly IResourceManager _resourceManager = default!; private readonly ILogger _logger = LoggingManager.GetLogger("renderer"); private readonly ILogger _loggerOpenGL = LoggingManager.GetLogger("open_gl")!; @@ -93,7 +95,7 @@ private void OnStartup(RuntimeStartupEvent args) break; } - var windowIcons = _windowManager.LoadWindowIcon(_textureManager, "Resources/Icons").ToList(); + var windowIcons = _windowManager.LoadWindowIcon(_textureManager, _resourceManager, "/Icons").ToList(); _windowManager.SetWindowIcons(MainWindow, windowIcons); diff --git a/Hypercube.Client/Graphics/Shading/Shader.cs b/Hypercube.Client/Graphics/Shading/Shader.cs index 347dd03..7e19faa 100644 --- a/Hypercube.Client/Graphics/Shading/Shader.cs +++ b/Hypercube.Client/Graphics/Shading/Shader.cs @@ -1,4 +1,6 @@ using Hypercube.Shared.Math.Vector; +using Hypercube.Shared.Resources; +using Hypercube.Shared.Resources.Manager; using OpenToolkit.Graphics.OpenGL4; using Vector2 = Hypercube.Shared.Math.Vector.Vector2; @@ -8,15 +10,24 @@ public class Shader : IShader { public readonly int _handle; private readonly Dictionary _uniformLocations = new(); - - public Shader(string path) : this($"{path}.vert", $"{path}.frag") - { - } - private Shader(string vertPath, string fragPath) + public Shader(ResourcePath folderPath, IResourceManager resourceManager) { - var vertexShader = CreateShader(vertPath, ShaderType.VertexShader); - var fragmentShader = CreateShader(fragPath, ShaderType.FragmentShader); + var files = resourceManager.FindContentFiles(folderPath).ToArray(); + Console.WriteLine(files.Length); + if (files.Length != 2) + throw new ArgumentException($"Shader folder contains more than 2 files: {folderPath.Path}"); + var vertPath = files.FirstOrDefault(f => f.Extension == ".vert"); + var fragPath = files.FirstOrDefault(f => f.Extension == ".frag"); + + if (vertPath.Path == null || fragPath.Path == null) + throw new ArgumentException("Shader folder doesn't contain one of the shaders"); + + var vertSource = resourceManager.ReadFileContentAllText(vertPath); + var fragSource = resourceManager.ReadFileContentAllText(fragPath); + + var vertexShader = CreateShader(vertSource, ShaderType.VertexShader); + var fragmentShader = CreateShader(fragSource, ShaderType.FragmentShader); _handle = GL.CreateProgram(); @@ -75,9 +86,8 @@ public void SetUniform(string name, Vector2 value) GL.Uniform2(_uniformLocations[name], value.X, value.Y); } - private int CreateShader(string path, ShaderType type) + private int CreateShader(string source, ShaderType type) { - var source = File.ReadAllText(path); var shader = GL.CreateShader(type); GL.ShaderSource(shader, source); diff --git a/Hypercube.Client/Graphics/Texturing/ITextureManager.cs b/Hypercube.Client/Graphics/Texturing/ITextureManager.cs index 75cc0ce..54c17b5 100644 --- a/Hypercube.Client/Graphics/Texturing/ITextureManager.cs +++ b/Hypercube.Client/Graphics/Texturing/ITextureManager.cs @@ -1,16 +1,18 @@ -namespace Hypercube.Client.Graphics.Texturing; +using Hypercube.Shared.Resources; + +namespace Hypercube.Client.Graphics.Texturing; public interface ITextureManager { - ITexture Create(string path); + ITexture Create(ResourcePath path); /// /// Creates ITexture, allows to set flipping mode /// /// Path to image /// DO FLIP /// ITexture - ITexture Create(string path, bool doFlip); + ITexture Create(ResourcePath path, bool doFlip); - ITextureHandle CreateHandler(string path); + ITextureHandle CreateHandler(ResourcePath path); ITextureHandle CreateHandler(ITexture texture); } \ No newline at end of file diff --git a/Hypercube.Client/Graphics/Texturing/TextureManager.cs b/Hypercube.Client/Graphics/Texturing/TextureManager.cs index b241759..325dd33 100644 --- a/Hypercube.Client/Graphics/Texturing/TextureManager.cs +++ b/Hypercube.Client/Graphics/Texturing/TextureManager.cs @@ -1,21 +1,25 @@ -using StbImageSharp; +using Hypercube.Shared.Dependency; +using Hypercube.Shared.Resources; +using Hypercube.Shared.Resources.Manager; +using StbImageSharp; namespace Hypercube.Client.Graphics.Texturing; public sealed class TextureManager : ITextureManager { + [Dependency] private readonly IResourceManager _resMan = default!; + public TextureManager() { StbImage.stbi_set_flip_vertically_on_load(1); } - public ITexture Create(string path) + public ITexture Create(ResourcePath path) { - using var stream = File.OpenRead(path); - return Create(ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha)); + return Create(ImageResult.FromStream(_resMan.ReadFileContent(path) ?? throw new FileNotFoundException(), ColorComponents.RedGreenBlueAlpha)); } - public ITexture Create(string path, bool doFlip) + public ITexture Create(ResourcePath path, bool doFlip) { if (doFlip) StbImage.stbi_set_flip_vertically_on_load(0); @@ -30,7 +34,7 @@ public ITextureHandle CreateHandler(ITexture texture) return new TextureHandle(texture); } - public ITextureHandle CreateHandler(string path) + public ITextureHandle CreateHandler(ResourcePath path) { return CreateHandler(Create(path)); } diff --git a/Hypercube.Client/Graphics/Windows/Manager/GlfwWindowManager.Window.cs b/Hypercube.Client/Graphics/Windows/Manager/GlfwWindowManager.Window.cs index 3f22442..c80f686 100644 --- a/Hypercube.Client/Graphics/Windows/Manager/GlfwWindowManager.Window.cs +++ b/Hypercube.Client/Graphics/Windows/Manager/GlfwWindowManager.Window.cs @@ -4,6 +4,8 @@ using Hypercube.Client.Graphics.Texturing; using Hypercube.Client.Graphics.Windows.Manager.Registrations; using Hypercube.Client.Utilities; +using Hypercube.Shared.Resources; +using Hypercube.Shared.Resources.Manager; using OpenTK.Windowing.Common; using OpenTK.Windowing.GraphicsLibraryFramework; using GlfwImage = OpenTK.Windowing.GraphicsLibraryFramework.Image; @@ -186,9 +188,9 @@ private bool TryGetWindow(Window* window, [NotNullWhen(true)] out GlfwWindowRegi return null; } - public IEnumerable LoadWindowIcon(ITextureManager textureMan, string resPath) + public IEnumerable LoadWindowIcon(ITextureManager textureMan, IResourceManager resourceMan, ResourcePath path) { - var files = Directory.EnumerateFiles(resPath, "*.png"); + var files = resourceMan.FindContentFiles(path); foreach (var file in files) { diff --git a/Hypercube.Client/Graphics/Windows/Manager/IWindowManager.cs b/Hypercube.Client/Graphics/Windows/Manager/IWindowManager.cs index b9a4b3d..3f3fbb3 100644 --- a/Hypercube.Client/Graphics/Windows/Manager/IWindowManager.cs +++ b/Hypercube.Client/Graphics/Windows/Manager/IWindowManager.cs @@ -2,6 +2,8 @@ using Hypercube.Client.Graphics.OpenGL; using Hypercube.Client.Graphics.Texturing; using Hypercube.Shared.Math.Vector; +using Hypercube.Shared.Resources; +using Hypercube.Shared.Resources.Manager; namespace Hypercube.Client.Graphics.Windows.Manager; @@ -30,7 +32,7 @@ public interface IWindowManager : IDisposable void WindowSetVisible(WindowRegistration registration, bool visible); void WindowSetSize(WindowRegistration registration, Vector2Int size); void WindowSwapBuffers(WindowRegistration window); - IEnumerable LoadWindowIcon(ITextureManager textureMan, string resPath); + IEnumerable LoadWindowIcon(ITextureManager textureMan, IResourceManager resourceManager, ResourcePath resPath); void SetWindowIcons(WindowRegistration window, List images); nint GetProcAddress(string procName); diff --git a/Hypercube.Shared/Resources/DirRoot/DirContentRoot.cs b/Hypercube.Shared/Resources/DirRoot/DirContentRoot.cs new file mode 100644 index 0000000..c5df94a --- /dev/null +++ b/Hypercube.Shared/Resources/DirRoot/DirContentRoot.cs @@ -0,0 +1,63 @@ +using System.Diagnostics.CodeAnalysis; +using Hypercube.Shared.Logging; +using Hypercube.Shared.Utilities.Helpers; + +namespace Hypercube.Shared.Resources.DirRoot; + +public class DirContentRoot : IContentRoot +{ + private readonly DirectoryInfo _directory; + private Logger _logger; + + public DirContentRoot(DirectoryInfo directory, Logger logger) + { + _directory = directory; + _logger = logger; + } + + public bool TryGetFile(ResourcePath path, [NotNullWhen(true)] out Stream? stream) + { + if (!FileExists(path)) + { + stream = null; + return false; + } + + try + { + stream = File.Open(GetPath(path), FileMode.Open, FileAccess.Read); + return true; + } + catch (Exception ex) + { + _logger.Error(ex.Message); + stream = null; + } + return false; + } + private bool FileExists(ResourcePath relPath) + { + var path = GetPath(relPath); + return File.Exists(path); + } + private string GetPath(ResourcePath path) + { + return Path.GetFullPath(Path.Combine(_directory.FullName, path)); + } + + public IEnumerable FindFiles(ResourcePath path) + { + var fullPath = GetPath(path); + if (!Directory.Exists(fullPath)) + { + yield break; + } + var paths = PathHelpers.GetFiles(fullPath); + + foreach (var filePath in paths) + { + var relPath = filePath.Substring(_directory.FullName.Length); + yield return ResourcePath.FromRelativeSystemPath(relPath, OperatingSystem.IsWindows() ? '\\' : '/'); + } + } +} \ No newline at end of file diff --git a/Hypercube.Shared/Resources/IContentRoot.cs b/Hypercube.Shared/Resources/IContentRoot.cs new file mode 100644 index 0000000..16f01e8 --- /dev/null +++ b/Hypercube.Shared/Resources/IContentRoot.cs @@ -0,0 +1,9 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Hypercube.Shared.Resources; + +public interface IContentRoot +{ + bool TryGetFile(ResourcePath path, [NotNullWhen(true)] out Stream? stream); + IEnumerable FindFiles(ResourcePath path); +} \ No newline at end of file diff --git a/Hypercube.Shared/Resources/Manager/IResourceManager.cs b/Hypercube.Shared/Resources/Manager/IResourceManager.cs index b5c9d0f..ea58bf0 100644 --- a/Hypercube.Shared/Resources/Manager/IResourceManager.cs +++ b/Hypercube.Shared/Resources/Manager/IResourceManager.cs @@ -1,6 +1,14 @@ -namespace Hypercube.Shared.Resources.Manager; +using System.Diagnostics.CodeAnalysis; -public class IResourceManager +namespace Hypercube.Shared.Resources.Manager; + +public interface IResourceManager { - + StreamReader WrapStream(Stream stream); + void AddRoot(ResourcePath prefix, IContentRoot root); + void MountContentFolder(string file, ResourcePath? prefix = null); + Stream? ReadFileContent(ResourcePath path); + bool TryReadFileContent(ResourcePath path, [NotNullWhen(true)] out Stream? fileStream); + IEnumerable FindContentFiles(ResourcePath? path); + string ReadFileContentAllText(ResourcePath path); } \ No newline at end of file diff --git a/Hypercube.Shared/Resources/Manager/ResourceManager.cs b/Hypercube.Shared/Resources/Manager/ResourceManager.cs index 783b1c5..5eaaaa6 100644 --- a/Hypercube.Shared/Resources/Manager/ResourceManager.cs +++ b/Hypercube.Shared/Resources/Manager/ResourceManager.cs @@ -1,6 +1,140 @@ -namespace Hypercube.Shared.Resources.Manager; +using System.Diagnostics.CodeAnalysis; +using Hypercube.Shared.Logging; +using Hypercube.Shared.Resources.DirRoot; +using Hypercube.Shared.Utilities.Helpers; + +namespace Hypercube.Shared.Resources.Manager; public class ResourceManager : IResourceManager { + private Logger _logger = LoggingManager.GetLogger("resources"); + private (ResourcePath prefix, IContentRoot root)[] _roots = []; + private readonly object _rootLock = new(); + public void AddRoot(ResourcePath prefix, IContentRoot root) + { + lock (_rootLock) + { + var copy = _roots; + Array.Resize(ref copy, copy.Length + 1); + copy[^1] = (prefix, root); + _roots = copy; + } + } + + public void MountContentFolder(string file, ResourcePath? prefix = null) + { + prefix = ValidatePrefix(prefix); + + if (!Path.IsPathRooted(file)) + file = PathHelpers.GetExecRelativeFile(file); + + var dirInfo = new DirectoryInfo(file); + if (!dirInfo.Exists) + { + throw new DirectoryNotFoundException($"Willing directory is not found. {dirInfo.FullName}"); + } + + var dirRoot = new DirContentRoot(dirInfo, LoggingManager.GetLogger("dirRoot")); + AddRoot(prefix.Value, dirRoot); + } + public bool TryReadFileContent(ResourcePath path, [NotNullWhen(true)] out Stream? fileStream) + { + if (!path.Rooted) + throw new ArgumentException($"Path must be rooted: {path}"); + + if (path.Path.EndsWith(ResourcePath.Separator)) + { + fileStream = null; + return false; + } + + try + { + foreach (var (prefix, root) in _roots) + { + if (!path.TryRelativeTo(prefix, out var relative)) + continue; + + if (root.TryGetFile(relative.Value, out var stream)) + { + fileStream = stream; + return true; + } + } + } + catch (Exception ex) + { + _logger.Error(ex.Message); + } + + fileStream = null; + return false; + } + + public IEnumerable FindContentFiles(ResourcePath? path) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + if (!path.Value.Rooted) + { + throw new ArgumentException("Path is not rooted", nameof(path)); + } + + var returned = new HashSet(); + + foreach (var (prefix, root) in _roots) + { + if (!path.Value.TryRelativeTo(prefix, out var relative)) + { + continue; + } + + foreach (var filename in root.FindFiles(relative.Value)) + { + var newPath = prefix + filename; + if (returned.Add(newPath)) + { + yield return newPath; + } + } + } + } + + public string ReadFileContentAllText(ResourcePath path) + { + var stream = ReadFileContent(path); + if (stream == null) + throw new ArgumentException("File not found"); + + return WrapStream(stream).ReadToEnd(); + } + + public Stream? ReadFileContent(ResourcePath path) + { + if (!TryReadFileContent(path, out var stream)) + return null; + + return stream; + } + + private ResourcePath ValidatePrefix(ResourcePath? prefix) + { + if (prefix is null) + prefix = "/"; + else if (!prefix.Value.Rooted) + { + throw new ArgumentException("Prefix must be rooted.", nameof(prefix)); + } + + return prefix.Value; + } + + public StreamReader WrapStream(Stream stream) + { + return new StreamReader(stream); + } } \ No newline at end of file diff --git a/Hypercube.Shared/Resources/ResourcePath.cs b/Hypercube.Shared/Resources/ResourcePath.cs index 2fd2432..b053ba5 100644 --- a/Hypercube.Shared/Resources/ResourcePath.cs +++ b/Hypercube.Shared/Resources/ResourcePath.cs @@ -2,12 +2,14 @@ namespace Hypercube.Shared.Resources; -public struct ResourcePath +public readonly struct ResourcePath { public const char Separator = '/'; public const string SeparatorStr = "/"; public const char WinSeparator = '\\'; public const string WinSeparatorStr = @"\"; + public static readonly char SystemSeparator; + public static readonly string SystemSeparatorStr; public static readonly ResourcePath Self = "."; @@ -23,6 +25,12 @@ public ResourcePath(string path) Path = path; } + static ResourcePath() + { + SystemSeparator = OperatingSystem.IsWindows() ? '\\' : '/'; + SystemSeparatorStr = OperatingSystem.IsWindows() ? "\\" : "/"; + } + public readonly string Path { get; } public bool Rooted => Path.Length > 0 && Path[0] == Separator; @@ -90,6 +98,10 @@ public static implicit operator ResourcePath(string path) { return new ResourcePath(path); } + public static implicit operator string(ResourcePath path) + { + return path.Path; + } public static ResourcePath operator +(ResourcePath l, ResourcePath r) { @@ -107,6 +119,36 @@ public static implicit operator ResourcePath(string path) return new ResourcePath(l.Path + "/" + r.Path); } + public bool TryRelativeTo(ResourcePath basePath, [NotNullWhen(true)] out ResourcePath? relative) + { + if (this == basePath) + { + relative = Self; + return true; + } + + if (basePath == Self && Relative) + { + relative = this; + return true; + } + + if (Path.StartsWith(basePath.Path)) + { + var x = Path[basePath.Path.Length..].Trim('/'); + relative = x == "" ? Self : new ResourcePath(x); + return true; + } + + relative = null; + return false; + } + + public static ResourcePath FromRelativeSystemPath(string path, char newSeparator) + { + // ReSharper disable once RedundantArgumentDefaultValue + return new ResourcePath(path.Replace(newSeparator, '/')); + } public static bool Equals(ResourcePath a, ResourcePath b) { diff --git a/Hypercube.Shared/Utilities/Helpers/PathHelpers.cs b/Hypercube.Shared/Utilities/Helpers/PathHelpers.cs new file mode 100644 index 0000000..2e2fad0 --- /dev/null +++ b/Hypercube.Shared/Utilities/Helpers/PathHelpers.cs @@ -0,0 +1,25 @@ +using System.Reflection; + +namespace Hypercube.Shared.Utilities.Helpers; + +public static class PathHelpers +{ + public static string GetExecDirectory() + { + return AppDomain.CurrentDomain.BaseDirectory; + } + + public static string GetExecRelativeFile(string file) + { + return Path.GetFullPath(Path.Combine(GetExecDirectory(), file)); + } + + public static IEnumerable GetFiles(string path) + { + return Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories); + } + + public static bool IsFileSystemCaseSensitive() => + !OperatingSystem.IsWindows() + && !OperatingSystem.IsMacOS(); +} \ No newline at end of file diff --git a/Hypercube.UnitTests/ResourceManager/ResourceManagerTests.cs b/Hypercube.UnitTests/ResourceManager/ResourceManagerTests.cs new file mode 100644 index 0000000..c66a637 --- /dev/null +++ b/Hypercube.UnitTests/ResourceManager/ResourceManagerTests.cs @@ -0,0 +1,22 @@ +namespace Hypercube.UnitTests.ResourceManager; + +public class ResourceManagerTests +{ + [Test] + public void ReadFileTest() + { + var resourceMan = new Shared.Resources.Manager.ResourceManager(); + resourceMan.MountContentFolder("Resources", "/"); + + if (!resourceMan.TryReadFileContent("/Tests/testFile.txt", out var stream)) + { + Assert.Fail("Unable to read file"); + return; + } + + var wrapped = resourceMan.WrapStream(stream); + var read = wrapped.ReadToEnd(); + Assert.That(read == "hey"); + Assert.Pass("Read file successfully"); + } +} \ No newline at end of file diff --git a/Hypercube.UnitTests/ResourceManager/ResourcePathTest.cs b/Hypercube.UnitTests/ResourceManager/ResourcePathTest.cs index 8246447..4fc9299 100644 --- a/Hypercube.UnitTests/ResourceManager/ResourcePathTest.cs +++ b/Hypercube.UnitTests/ResourceManager/ResourcePathTest.cs @@ -29,6 +29,14 @@ public void RootedTest() Assert.Pass("Res path is rooted"); } + [Test] + public void ParentDirTest() + { + var resPath = new ResourcePath("/Rooted/path.txt"); + Assert.That(resPath.ParentDirectory.Path == "/Rooted"); + Assert.Pass("Parent directory passed"); + } + [Test] public void PathConcatTest() { diff --git a/Resources/Shaders/base.frag b/Resources/Shaders/base/base.frag similarity index 100% rename from Resources/Shaders/base.frag rename to Resources/Shaders/base/base.frag diff --git a/Resources/Shaders/base.vert b/Resources/Shaders/base/base.vert similarity index 100% rename from Resources/Shaders/base.vert rename to Resources/Shaders/base/base.vert From 2c42392394097bb1e275bc8e5b016ab2d64612cb Mon Sep 17 00:00:00 2001 From: JerryImMouse Date: Thu, 11 Jul 2024 06:31:53 +0500 Subject: [PATCH 3/8] oh, forgot about this --- Resources/Tests/testFile.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 Resources/Tests/testFile.txt diff --git a/Resources/Tests/testFile.txt b/Resources/Tests/testFile.txt new file mode 100644 index 0000000..c09a35f --- /dev/null +++ b/Resources/Tests/testFile.txt @@ -0,0 +1 @@ +hey \ No newline at end of file From 4dcc99f9398b3053ed642008e1ae156f18f77216 Mon Sep 17 00:00:00 2001 From: JerryImMouse Date: Thu, 11 Jul 2024 06:34:54 +0500 Subject: [PATCH 4/8] tornado review --- Hypercube.Shared/Resources/ResourcePath.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Hypercube.Shared/Resources/ResourcePath.cs b/Hypercube.Shared/Resources/ResourcePath.cs index b053ba5..3dd1076 100644 --- a/Hypercube.Shared/Resources/ResourcePath.cs +++ b/Hypercube.Shared/Resources/ResourcePath.cs @@ -42,7 +42,7 @@ public string FilenameWithExt get { var sepIndex = Path.LastIndexOf('/') + 1; - return sepIndex == -1 ? "" : Path[sepIndex..]; + return sepIndex == -1 ? string.Empty : Path[sepIndex..]; } } @@ -77,10 +77,8 @@ public ResourcePath ParentDirectory get { if (IsSelf) - { return Self; - } - + var idx = Path.Length > 1 && Path[^1] == '/' ? Path[..^1].LastIndexOf('/') : Path.LastIndexOf('/'); @@ -107,17 +105,18 @@ public static implicit operator string(ResourcePath path) { if (r.IsSelf) return l; + if (r.Rooted) return r; - if (l.Path == "") + + if (l.Path == string.Empty) return new ResourcePath("/" + r.Path); if (l.Path.EndsWith("/")) - { return new ResourcePath(l.Path + r.Path); - } + - return new ResourcePath(l.Path + "/" + r.Path); + return new ResourcePath($"{l.Path}/{r.Path}"); } public bool TryRelativeTo(ResourcePath basePath, [NotNullWhen(true)] out ResourcePath? relative) { @@ -136,7 +135,7 @@ public bool TryRelativeTo(ResourcePath basePath, [NotNullWhen(true)] out Resourc if (Path.StartsWith(basePath.Path)) { var x = Path[basePath.Path.Length..].Trim('/'); - relative = x == "" ? Self : new ResourcePath(x); + relative = x == string.Empty ? Self : new ResourcePath(x); return true; } From 4c387249db49b95481a842b23d826fe100b41963 Mon Sep 17 00:00:00 2001 From: JerryImMouse Date: Thu, 11 Jul 2024 06:41:20 +0500 Subject: [PATCH 5/8] remove debug shit, show capabilities --- .../Graphics/Rendering/Renderer.Render.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs b/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs index 157f158..3de0504 100644 --- a/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs +++ b/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs @@ -31,12 +31,14 @@ public sealed partial class Renderer private void OnLoad() { + // mount directories + _resourceManager.MountContentFolder(".", "/"); _resourceManager.MountContentFolder("Resources", "/"); - var read = _resourceManager.WrapStream(_resourceManager.ReadFileContent("/Debug/test.txt")!).ReadToEnd(); - Console.WriteLine(read); - Console.WriteLine(read); - _baseShader = new Shader("/Shaders/base/", _resourceManager); - _baseTexture = _textureManager.CreateHandler("/Textures/icon.png"); + _resourceManager.MountContentFolder("Resources/Textures", "/"); + _resourceManager.MountContentFolder("Resources/Shaders", "/"); + + _baseShader = new Shader("/base/", _resourceManager); + _baseTexture = _textureManager.CreateHandler("/icon.png"); _baseTexture.Bind(); _vbo = new BufferObject(BufferTarget.ArrayBuffer); From cd186d133ad8e28e016c4ddd1fd007bc83353dbb Mon Sep 17 00:00:00 2001 From: JerryImMouse Date: Thu, 11 Jul 2024 07:16:35 +0500 Subject: [PATCH 6/8] probably thats all? --- .../Graphics/Rendering/Renderer.Render.cs | 3 +- Hypercube.Client/Graphics/Shading/Shader.cs | 17 ++++------ .../Graphics/Texturing/TextureManager.cs | 4 +-- .../Manager/GlfwWindowManager.Window.cs | 4 +-- .../Windows/Manager/IWindowManager.cs | 2 +- .../Resources/DirRoot/DirContentRoot.cs | 5 +-- .../Resources/Manager/ResourceManager.cs | 33 ++++++++----------- 7 files changed, 28 insertions(+), 40 deletions(-) diff --git a/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs b/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs index 3de0504..7150cca 100644 --- a/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs +++ b/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs @@ -1,7 +1,6 @@ using Hypercube.Client.Graphics.Shading; using Hypercube.Client.Graphics.Texturing; using Hypercube.Client.Graphics.Viewports; -using Hypercube.Client.Graphics.Windows; using Hypercube.Shared.Math; using Hypercube.Shared.Math.Box; using Hypercube.Shared.Runtimes.Loop.Event; @@ -37,7 +36,7 @@ private void OnLoad() _resourceManager.MountContentFolder("Resources/Textures", "/"); _resourceManager.MountContentFolder("Resources/Shaders", "/"); - _baseShader = new Shader("/base/", _resourceManager); + _baseShader = new Shader("base", _resourceManager); _baseTexture = _textureManager.CreateHandler("/icon.png"); _baseTexture.Bind(); diff --git a/Hypercube.Client/Graphics/Shading/Shader.cs b/Hypercube.Client/Graphics/Shading/Shader.cs index 7e19faa..9ed479c 100644 --- a/Hypercube.Client/Graphics/Shading/Shader.cs +++ b/Hypercube.Client/Graphics/Shading/Shader.cs @@ -11,18 +11,13 @@ public class Shader : IShader public readonly int _handle; private readonly Dictionary _uniformLocations = new(); - public Shader(ResourcePath folderPath, IResourceManager resourceManager) + public Shader(string path, IResourceManager manager) : this(new ResourcePath($"{path}.vert"), new ResourcePath($"{path}.frag"), + manager) + { + } + + private Shader(ResourcePath vertPath, ResourcePath fragPath, IResourceManager resourceManager) { - var files = resourceManager.FindContentFiles(folderPath).ToArray(); - Console.WriteLine(files.Length); - if (files.Length != 2) - throw new ArgumentException($"Shader folder contains more than 2 files: {folderPath.Path}"); - var vertPath = files.FirstOrDefault(f => f.Extension == ".vert"); - var fragPath = files.FirstOrDefault(f => f.Extension == ".frag"); - - if (vertPath.Path == null || fragPath.Path == null) - throw new ArgumentException("Shader folder doesn't contain one of the shaders"); - var vertSource = resourceManager.ReadFileContentAllText(vertPath); var fragSource = resourceManager.ReadFileContentAllText(fragPath); diff --git a/Hypercube.Client/Graphics/Texturing/TextureManager.cs b/Hypercube.Client/Graphics/Texturing/TextureManager.cs index 325dd33..c3642a0 100644 --- a/Hypercube.Client/Graphics/Texturing/TextureManager.cs +++ b/Hypercube.Client/Graphics/Texturing/TextureManager.cs @@ -7,7 +7,7 @@ namespace Hypercube.Client.Graphics.Texturing; public sealed class TextureManager : ITextureManager { - [Dependency] private readonly IResourceManager _resMan = default!; + [Dependency] private readonly IResourceManager _resourceManager = default!; public TextureManager() { @@ -16,7 +16,7 @@ public TextureManager() public ITexture Create(ResourcePath path) { - return Create(ImageResult.FromStream(_resMan.ReadFileContent(path) ?? throw new FileNotFoundException(), ColorComponents.RedGreenBlueAlpha)); + return Create(ImageResult.FromStream(_resourceManager.ReadFileContent(path) ?? throw new FileNotFoundException(), ColorComponents.RedGreenBlueAlpha)); } public ITexture Create(ResourcePath path, bool doFlip) diff --git a/Hypercube.Client/Graphics/Windows/Manager/GlfwWindowManager.Window.cs b/Hypercube.Client/Graphics/Windows/Manager/GlfwWindowManager.Window.cs index c80f686..884428e 100644 --- a/Hypercube.Client/Graphics/Windows/Manager/GlfwWindowManager.Window.cs +++ b/Hypercube.Client/Graphics/Windows/Manager/GlfwWindowManager.Window.cs @@ -188,9 +188,9 @@ private bool TryGetWindow(Window* window, [NotNullWhen(true)] out GlfwWindowRegi return null; } - public IEnumerable LoadWindowIcon(ITextureManager textureMan, IResourceManager resourceMan, ResourcePath path) + public IEnumerable LoadWindowIcon(ITextureManager textureMan, IResourceManager resourceManager, ResourcePath path) { - var files = resourceMan.FindContentFiles(path); + var files = resourceManager.FindContentFiles(path); foreach (var file in files) { diff --git a/Hypercube.Client/Graphics/Windows/Manager/IWindowManager.cs b/Hypercube.Client/Graphics/Windows/Manager/IWindowManager.cs index 3f3fbb3..65bde37 100644 --- a/Hypercube.Client/Graphics/Windows/Manager/IWindowManager.cs +++ b/Hypercube.Client/Graphics/Windows/Manager/IWindowManager.cs @@ -32,7 +32,7 @@ public interface IWindowManager : IDisposable void WindowSetVisible(WindowRegistration registration, bool visible); void WindowSetSize(WindowRegistration registration, Vector2Int size); void WindowSwapBuffers(WindowRegistration window); - IEnumerable LoadWindowIcon(ITextureManager textureMan, IResourceManager resourceManager, ResourcePath resPath); + IEnumerable LoadWindowIcon(ITextureManager textureManager, IResourceManager resourceManager, ResourcePath resPath); void SetWindowIcons(WindowRegistration window, List images); nint GetProcAddress(string procName); diff --git a/Hypercube.Shared/Resources/DirRoot/DirContentRoot.cs b/Hypercube.Shared/Resources/DirRoot/DirContentRoot.cs index c5df94a..9489f62 100644 --- a/Hypercube.Shared/Resources/DirRoot/DirContentRoot.cs +++ b/Hypercube.Shared/Resources/DirRoot/DirContentRoot.cs @@ -35,11 +35,13 @@ public bool TryGetFile(ResourcePath path, [NotNullWhen(true)] out Stream? stream } return false; } + private bool FileExists(ResourcePath relPath) { var path = GetPath(relPath); return File.Exists(path); } + private string GetPath(ResourcePath path) { return Path.GetFullPath(Path.Combine(_directory.FullName, path)); @@ -49,9 +51,8 @@ public IEnumerable FindFiles(ResourcePath path) { var fullPath = GetPath(path); if (!Directory.Exists(fullPath)) - { yield break; - } + var paths = PathHelpers.GetFiles(fullPath); foreach (var filePath in paths) diff --git a/Hypercube.Shared/Resources/Manager/ResourceManager.cs b/Hypercube.Shared/Resources/Manager/ResourceManager.cs index 5eaaaa6..b33f12d 100644 --- a/Hypercube.Shared/Resources/Manager/ResourceManager.cs +++ b/Hypercube.Shared/Resources/Manager/ResourceManager.cs @@ -7,9 +7,10 @@ namespace Hypercube.Shared.Resources.Manager; public class ResourceManager : IResourceManager { - private Logger _logger = LoggingManager.GetLogger("resources"); - private (ResourcePath prefix, IContentRoot root)[] _roots = []; + private readonly Logger _logger = LoggingManager.GetLogger("resources"); + private (ResourcePath prefix, IContentRoot root)[] _roots = Array.Empty<(ResourcePath, IContentRoot)>(); private readonly object _rootLock = new(); + public void AddRoot(ResourcePath prefix, IContentRoot root) { lock (_rootLock) @@ -29,11 +30,10 @@ public void MountContentFolder(string file, ResourcePath? prefix = null) file = PathHelpers.GetExecRelativeFile(file); var dirInfo = new DirectoryInfo(file); + if (!dirInfo.Exists) - { throw new DirectoryNotFoundException($"Willing directory is not found. {dirInfo.FullName}"); - } - + var dirRoot = new DirContentRoot(dirInfo, LoggingManager.GetLogger("dirRoot")); AddRoot(prefix.Value, dirRoot); } @@ -74,32 +74,27 @@ public bool TryReadFileContent(ResourcePath path, [NotNullWhen(true)] out Stream public IEnumerable FindContentFiles(ResourcePath? path) { - if (path == null) - { + if (path is null) throw new ArgumentNullException(nameof(path)); - } - + if (!path.Value.Rooted) - { throw new ArgumentException("Path is not rooted", nameof(path)); - } + var returned = new HashSet(); foreach (var (prefix, root) in _roots) { if (!path.Value.TryRelativeTo(prefix, out var relative)) - { continue; - } - + foreach (var filename in root.FindFiles(relative.Value)) { var newPath = prefix + filename; + if (returned.Add(newPath)) - { yield return newPath; - } + } } } @@ -107,7 +102,7 @@ public IEnumerable FindContentFiles(ResourcePath? path) public string ReadFileContentAllText(ResourcePath path) { var stream = ReadFileContent(path); - if (stream == null) + if (stream is null) throw new ArgumentException("File not found"); return WrapStream(stream).ReadToEnd(); @@ -126,10 +121,8 @@ private ResourcePath ValidatePrefix(ResourcePath? prefix) if (prefix is null) prefix = "/"; else if (!prefix.Value.Rooted) - { throw new ArgumentException("Prefix must be rooted.", nameof(prefix)); - } - + return prefix.Value; } From 598aa687e67feaa7aa19f2f82027cb44112543ec Mon Sep 17 00:00:00 2001 From: JerryImMouse Date: Thu, 11 Jul 2024 07:41:07 +0500 Subject: [PATCH 7/8] tornado review --- .../Graphics/Rendering/Renderer.Render.cs | 2 +- .../Resources/Manager/ResourceManager.cs | 13 +++++-------- Hypercube.Shared/Resources/ResourcePath.cs | 13 ++++++------- Hypercube.Shared/Utilities/Helpers/PathHelpers.cs | 7 ++++--- Resources/Shaders/{base => }/base.frag | 0 Resources/Shaders/{base => }/base.vert | 0 6 files changed, 16 insertions(+), 19 deletions(-) rename Resources/Shaders/{base => }/base.frag (100%) rename Resources/Shaders/{base => }/base.vert (100%) diff --git a/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs b/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs index 7150cca..215ada9 100644 --- a/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs +++ b/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs @@ -36,7 +36,7 @@ private void OnLoad() _resourceManager.MountContentFolder("Resources/Textures", "/"); _resourceManager.MountContentFolder("Resources/Shaders", "/"); - _baseShader = new Shader("base", _resourceManager); + _baseShader = new Shader("/base", _resourceManager); _baseTexture = _textureManager.CreateHandler("/icon.png"); _baseTexture.Bind(); diff --git a/Hypercube.Shared/Resources/Manager/ResourceManager.cs b/Hypercube.Shared/Resources/Manager/ResourceManager.cs index b33f12d..92664b9 100644 --- a/Hypercube.Shared/Resources/Manager/ResourceManager.cs +++ b/Hypercube.Shared/Resources/Manager/ResourceManager.cs @@ -94,7 +94,6 @@ public IEnumerable FindContentFiles(ResourcePath? path) if (returned.Add(newPath)) yield return newPath; - } } } @@ -103,7 +102,7 @@ public string ReadFileContentAllText(ResourcePath path) { var stream = ReadFileContent(path); if (stream is null) - throw new ArgumentException("File not found"); + throw new ArgumentException($"File not found: {path.Path}"); return WrapStream(stream).ReadToEnd(); } @@ -118,12 +117,10 @@ public string ReadFileContentAllText(ResourcePath path) private ResourcePath ValidatePrefix(ResourcePath? prefix) { - if (prefix is null) - prefix = "/"; - else if (!prefix.Value.Rooted) - throw new ArgumentException("Prefix must be rooted.", nameof(prefix)); - - return prefix.Value; + if (prefix.HasValue && !prefix.Value.Rooted) + throw new ArgumentException("Prefix must be rooted", nameof(prefix)); + + return prefix ?? "/"; } public StreamReader WrapStream(Stream stream) diff --git a/Hypercube.Shared/Resources/ResourcePath.cs b/Hypercube.Shared/Resources/ResourcePath.cs index 3dd1076..485ca27 100644 --- a/Hypercube.Shared/Resources/ResourcePath.cs +++ b/Hypercube.Shared/Resources/ResourcePath.cs @@ -13,7 +13,12 @@ public readonly struct ResourcePath public static readonly ResourcePath Self = "."; - + static ResourcePath() + { + SystemSeparator = OperatingSystem.IsWindows() ? '\\' : '/'; + SystemSeparatorStr = OperatingSystem.IsWindows() ? "\\" : "/"; + } + public ResourcePath(string path) { if (OperatingSystem.IsWindows()) @@ -25,12 +30,6 @@ public ResourcePath(string path) Path = path; } - static ResourcePath() - { - SystemSeparator = OperatingSystem.IsWindows() ? '\\' : '/'; - SystemSeparatorStr = OperatingSystem.IsWindows() ? "\\" : "/"; - } - public readonly string Path { get; } public bool Rooted => Path.Length > 0 && Path[0] == Separator; diff --git a/Hypercube.Shared/Utilities/Helpers/PathHelpers.cs b/Hypercube.Shared/Utilities/Helpers/PathHelpers.cs index 2e2fad0..3067606 100644 --- a/Hypercube.Shared/Utilities/Helpers/PathHelpers.cs +++ b/Hypercube.Shared/Utilities/Helpers/PathHelpers.cs @@ -19,7 +19,8 @@ public static IEnumerable GetFiles(string path) return Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories); } - public static bool IsFileSystemCaseSensitive() => - !OperatingSystem.IsWindows() - && !OperatingSystem.IsMacOS(); + public static bool IsFileSystemCaseSensitive() + { + return !OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS(); + } } \ No newline at end of file diff --git a/Resources/Shaders/base/base.frag b/Resources/Shaders/base.frag similarity index 100% rename from Resources/Shaders/base/base.frag rename to Resources/Shaders/base.frag diff --git a/Resources/Shaders/base/base.vert b/Resources/Shaders/base.vert similarity index 100% rename from Resources/Shaders/base/base.vert rename to Resources/Shaders/base.vert From e36f3d67cc552201ad767355100bb28f7ecf29ae Mon Sep 17 00:00:00 2001 From: JerryImMouse Date: Thu, 11 Jul 2024 07:43:21 +0500 Subject: [PATCH 8/8] oh, forgot sealed --- Hypercube.Shared/Resources/Manager/ResourceManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hypercube.Shared/Resources/Manager/ResourceManager.cs b/Hypercube.Shared/Resources/Manager/ResourceManager.cs index 92664b9..03a697f 100644 --- a/Hypercube.Shared/Resources/Manager/ResourceManager.cs +++ b/Hypercube.Shared/Resources/Manager/ResourceManager.cs @@ -5,7 +5,7 @@ namespace Hypercube.Shared.Resources.Manager; -public class ResourceManager : IResourceManager +public sealed class ResourceManager : IResourceManager { private readonly Logger _logger = LoggingManager.GetLogger("resources"); private (ResourcePath prefix, IContentRoot root)[] _roots = Array.Empty<(ResourcePath, IContentRoot)>();