From 4b2bac7cd8d9acb5eef56d26c9b8a4c137e8a478 Mon Sep 17 00:00:00 2001 From: MapleWheels Date: Mon, 16 Dec 2024 16:30:13 -0500 Subject: [PATCH] [Milestone] StorageService completed. --- .../LuaCs/Services/StorageService.cs | 451 ++++++++++++++---- .../Services/_Interfaces/IStorageService.cs | 12 +- 2 files changed, 355 insertions(+), 108 deletions(-) diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/StorageService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/StorageService.cs index facf6b395..d20d77942 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/StorageService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/StorageService.cs @@ -12,6 +12,7 @@ using Barotrauma.LuaCs.Networking; using Barotrauma.Steam; using FluentResults; using FluentResults.LuaCs; +using Microsoft.CodeAnalysis; using OneOf.Types; using Error = FluentResults.Error; using File = Barotrauma.IO.File; @@ -64,110 +65,143 @@ public class StorageService : IStorageService _kLocalFilePathRules = null; } - public FluentResults.Result LoadLocalXml(ContentPackage package, string localFilePath) - { - var r = LoadLocalText(package, localFilePath); - if (r is { IsSuccess: true, Value: not null }) - return XDocument.Parse(r.Value); - else - { - return r.ToResult(s => null) - .WithError(GetGeneralError(nameof(LoadLocalXml), localFilePath, package)); - } - } + public FluentResults.Result LoadLocalXml(ContentPackage package, string localFilePath) => + GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? TryLoadXml(r.Value) : r.ToResult(); + public FluentResults.Result LoadLocalBinary(ContentPackage package, string localFilePath) => + GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? TryLoadBinary(r.Value) : r.ToResult(); + public FluentResults.Result LoadLocalText(ContentPackage package, string localFilePath) => + GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? TryLoadText(r.Value) : r.ToResult(); + public FluentResults.Result SaveLocalXml(ContentPackage package, string localFilePath, XDocument document) => + GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? TrySaveXml(r.Value, document) : r.ToResult(); + public FluentResults.Result SaveLocalBinary(ContentPackage package, string localFilePath, in byte[] bytes) => + GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? TrySaveBinary(r.Value, bytes) : r.ToResult(); + public FluentResults.Result SaveLocalText(ContentPackage package, string localFilePath, in string text) => + GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? TrySaveText(r.Value, text) : r.ToResult(); + public async Task> LoadLocalXmlAsync(ContentPackage package, string localFilePath) => + GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? await TryLoadXmlAsync(r.Value) : r.ToResult(); + public async Task> LoadLocalBinaryAsync(ContentPackage package, string localFilePath) => + GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? await TryLoadBinaryAsync(r.Value) : r.ToResult(); + public async Task> LoadLocalTextAsync(ContentPackage package, string localFilePath) => + GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? await TryLoadTextAsync(r.Value) : r.ToResult(); + public async Task SaveLocalXmlAsync(ContentPackage package, string localFilePath, XDocument document) => + GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? await TrySaveXmlAsync(r.Value, document) : r.ToResult(); + public async Task SaveLocalBinaryAsync(ContentPackage package, string localFilePath, byte[] bytes) => + GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? await TrySaveBinaryAsync(r.Value, bytes) : r.ToResult(); + public async Task SaveLocalTextAsync(ContentPackage package, string localFilePath, string text) => + GetAbsFromLocal(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? await TrySaveTextAsync(r.Value, text) : r.ToResult(); + public FluentResults.Result LoadPackageXml(ContentPackage package, string localFilePath) => + GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? TryLoadXml(r.Value) : r.ToResult(); + public FluentResults.Result LoadPackageBinary(ContentPackage package, string localFilePath) => + GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? TryLoadBinary(r.Value) : r.ToResult(); + public FluentResults.Result LoadPackageText(ContentPackage package, string localFilePath) => + GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? TryLoadText(r.Value) : r.ToResult(); - - public FluentResults.Result LoadLocalBinary(ContentPackage package, string localFilePath) => TryLoadBinary(GetAbsFromLocal(package, localFilePath)); - public FluentResults.Result LoadLocalText(ContentPackage package, string localFilePath) => TryLoadText(GetAbsFromLocal(package, localFilePath)); - public FluentResults.Result SaveLocalXml(ContentPackage package, string localFilePath, XDocument document) => TrySaveXml(GetAbsFromLocal(package, localFilePath), document); - - public FluentResults.Result SaveLocalBinary(ContentPackage package, string localFilePath, in byte[] bytes) => TrySaveBinary(GetAbsFromLocal(package, localFilePath), bytes); - - public FluentResults.Result SaveLocalText(ContentPackage package, string localFilePath, in string text) => TrySaveText(GetAbsFromLocal(package, localFilePath), text); - - public async Task> LoadLocalXmlAsync(ContentPackage package, string localFilePath) - { - var r = await LoadLocalTextAsync(package, localFilePath); - if (r is { IsSuccess: true, Value: not null }) - return XDocument.Parse(r.Value); - else - { - return r.ToResult(s => null) - .WithError(GetGeneralError(nameof(LoadLocalXml), localFilePath, package)); - } - } - - public Task> LoadLocalBinaryAsync(ContentPackage package, string localFilePath) => - TryLoadBinaryAsync(GetAbsFromLocal(package, localFilePath)); - public Task> LoadLocalTextAsync(ContentPackage package, string localFilePath) => TryLoadTextAsync(GetAbsFromLocal(package, localFilePath)); - public Task SaveLocalXmlAsync(ContentPackage package, string localFilePath, XDocument document) => TrySaveXmlAsync(GetAbsFromLocal(package, localFilePath), document); - public Task SaveLocalBinaryAsync(ContentPackage package, string localFilePath, byte[] bytes) => TrySaveBinaryAsync(GetAbsFromLocal(package, localFilePath), bytes); - public Task SaveLocalTextAsync(ContentPackage package, string localFilePath, string text) => TrySaveTextAsync(GetAbsFromLocal(package, localFilePath), text); - public FluentResults.Result LoadPackageXml(ContentPackage package, string localFilePath) => TryLoadXml(Path.GetFullPath(package.Path.CleanUpPath())); - public FluentResults.Result LoadPackageBinary(ContentPackage package, string localFilePath) => TryLoadBinary(Path.GetFullPath(package.Path.CleanUpPath())); - public FluentResults.Result LoadPackageText(ContentPackage package, string localFilePath) => TryLoadText(Path.GetFullPath(package.Path.CleanUpPath())); - public FluentResults.Result> LoadPackageXmlFiles(ContentPackage package, ImmutableArray localFilePaths) + public ImmutableArray<(string, FluentResults.Result)> LoadPackageXmlFiles(ContentPackage package, ImmutableArray localFilePaths) { ((IService)this).CheckDisposed(); if (localFilePaths.IsDefaultOrEmpty) - return new FluentResults.Result>().WithError(new ExceptionalError(new ArgumentNullException(nameof(localFilePaths)))); - var builder = ImmutableArray.CreateBuilder(); + return ImmutableArray<(string, FluentResults.Result)>.Empty; + var builder = ImmutableArray.CreateBuilder<(string, FluentResults.Result)>(localFilePaths.Length); foreach (var path in localFilePaths) - { - if (TryLoadXml(path) is { IsSuccess: true, Value: var document }) - { - - } - } + builder.Add((path, LoadPackageXml(package, path))); + return builder.MoveToImmutable(); } - public FluentResults.Result> LoadPackageBinaryFiles(ContentPackage package, ImmutableArray localFilePaths) + public ImmutableArray<(string, FluentResults.Result)> LoadPackageBinaryFiles(ContentPackage package, ImmutableArray localFilePaths) { ((IService)this).CheckDisposed(); - throw new NotImplementedException(); + if (localFilePaths.IsDefaultOrEmpty) + return ImmutableArray<(string, FluentResults.Result)>.Empty; + var builder = ImmutableArray.CreateBuilder<(string, FluentResults.Result)>(localFilePaths.Length); + foreach (var path in localFilePaths) + builder.Add((path, LoadPackageBinary(package, path))); + return builder.MoveToImmutable(); } - public FluentResults.Result> LoadPackageTextFiles(ContentPackage package, ImmutableArray localFilePaths) + public ImmutableArray<(string, FluentResults.Result)> LoadPackageTextFiles(ContentPackage package, ImmutableArray localFilePaths) { ((IService)this).CheckDisposed(); - throw new NotImplementedException(); + if (localFilePaths.IsDefaultOrEmpty) + return ImmutableArray<(string, FluentResults.Result)>.Empty; + var builder = ImmutableArray.CreateBuilder<(string, FluentResults.Result)>(localFilePaths.Length); + foreach (var path in localFilePaths) + builder.Add((path, LoadPackageText(package, path))); + return builder.MoveToImmutable(); } public FluentResults.Result> FindFilesInPackage(ContentPackage package, string localSubfolder, string regexFilter, bool searchRecursively) { ((IService)this).CheckDisposed(); - throw new NotImplementedException(); + var r = GetAbsFromPackage(package, localSubfolder); + if (r is { IsFailed: true }) + return r.ToResult(); + var builder = ImmutableArray.CreateBuilder<(string, FluentResults.Result>)>(); + var sOption = searchRecursively ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; + string[] arr = Directory.GetFiles(localSubfolder, regexFilter.IsNullOrWhiteSpace() ? "*.*" : regexFilter, sOption); + return new FluentResults.Result>().WithSuccess($"Files found.") + .WithValue(arr.ToImmutableArray()); } - public Task> LoadPackageXmlAsync(ContentPackage package, string localFilePath) + public async Task> LoadPackageXmlAsync(ContentPackage package, string localFilePath) => + GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? await TryLoadXmlAsync(r.Value) : r.ToResult(); + + public async Task> LoadPackageBinaryAsync(ContentPackage package, string localFilePath) => + GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? await TryLoadBinaryAsync(r.Value) : r.ToResult(); + + public async Task> LoadPackageTextAsync(ContentPackage package, string localFilePath) => + GetAbsFromPackage(package, localFilePath) is var r && r is { IsSuccess: true, Value: not null } + ? await TryLoadTextAsync(r.Value) : r.ToResult(); + + public async Task)>> LoadPackageXmlFilesAsync(ContentPackage package, ImmutableArray localFilePaths) { - throw new NotImplementedException(); + ((IService)this).CheckDisposed(); + if (localFilePaths.IsDefaultOrEmpty) + return ImmutableArray<(string, FluentResults.Result)>.Empty; + var builder = ImmutableArray.CreateBuilder<(string, FluentResults.Result)>(localFilePaths.Length); + foreach (var path in localFilePaths) + builder.Add((path, await LoadPackageXmlAsync(package, path))); + return builder.MoveToImmutable(); } - public Task> LoadPackageBinaryAsync(ContentPackage package, string localFilePath) + public async Task)>> LoadPackageBinaryFilesAsync(ContentPackage package, ImmutableArray localFilePaths) { - throw new NotImplementedException(); + ((IService)this).CheckDisposed(); + if (localFilePaths.IsDefaultOrEmpty) + return ImmutableArray<(string, FluentResults.Result)>.Empty; + var builder = ImmutableArray.CreateBuilder<(string, FluentResults.Result)>(localFilePaths.Length); + foreach (var path in localFilePaths) + builder.Add((path, await LoadPackageBinaryAsync(package, path))); + return builder.MoveToImmutable(); } - public Task> LoadPackageTextAsync(ContentPackage package, string localFilePath) + public async Task)>> LoadPackageTextFilesAsync(ContentPackage package, ImmutableArray localFilePaths) { - throw new NotImplementedException(); + ((IService)this).CheckDisposed(); + if (localFilePaths.IsDefaultOrEmpty) + return ImmutableArray<(string, FluentResults.Result)>.Empty; + var builder = ImmutableArray.CreateBuilder<(string, FluentResults.Result)>(localFilePaths.Length); + foreach (var path in localFilePaths) + builder.Add((path, await LoadPackageTextAsync(package, path))); + return builder.MoveToImmutable(); } - public Task>> LoadPackageXmlFilesAsync(ContentPackage package, ImmutableArray localFilePaths) - { - throw new NotImplementedException(); - } - - public Task>> LoadPackageBinaryFilesAsync(ContentPackage package, ImmutableArray localFilePaths) - { - throw new NotImplementedException(); - } - - public Task>> LoadPackageTextFilesAsync(ContentPackage package, ImmutableArray localFilePaths) - { - throw new NotImplementedException(); - } public FluentResults.Result TryLoadXml(string filePath, Encoding encoding = null) { @@ -206,63 +240,133 @@ public class StorageService : IStorageService }); } - public FluentResults.Result TrySaveXml(string filePath, in XDocument document, Encoding encoding = null) - { - ((IService)this).CheckDisposed(); - return IOExceptionsOperationRunner(nameof(TrySaveXml), filePath, () => - { - - }); - } - + public FluentResults.Result TrySaveXml(string filePath, in XDocument document, Encoding encoding = null) => TrySaveText(filePath, document.ToString(), encoding); public FluentResults.Result TrySaveText(string filePath, in string text, Encoding encoding = null) { ((IService)this).CheckDisposed(); - throw new NotImplementedException(); + if (text.IsNullOrWhiteSpace()) + { + return FluentResults.Result.Fail($"Contents are empty for {filePath}") + .WithError(new Error($"Contents are empty for {filePath}") + .WithMetadata(MetadataType.ExceptionObject, this) + .WithMetadata(MetadataType.Sources, filePath)); + } + + string t = text; //copy + return IOExceptionsOperationRunner(nameof(TrySaveText), filePath, () => + { + var fp = filePath.CleanUpPath(); + fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp); + System.IO.File.WriteAllText(fp, t, encoding); + return new FluentResults.Result().WithSuccess($"Saved to file successfully"); + }); } public FluentResults.Result TrySaveBinary(string filePath, in byte[] bytes) { ((IService)this).CheckDisposed(); - throw new NotImplementedException(); + if (bytes is null || bytes.Length == 0) + { + return FluentResults.Result.Fail($"Byte array is null or empty for {filePath}") + .WithError(new Error($"Byte array is null or empty for {filePath}") + .WithMetadata(MetadataType.ExceptionObject, this) + .WithMetadata(MetadataType.Sources, filePath)); + } + byte[] b = new byte[bytes.Length]; + System.Buffer.BlockCopy(bytes, 0, b, 0, bytes.Length); + return IOExceptionsOperationRunner(nameof(TrySaveBinary), filePath, () => + { + var fp = filePath.CleanUpPath(); + fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp); + System.IO.File.WriteAllBytes(fp, b); + return new FluentResults.Result().WithSuccess($"Saved to file successfully"); + }); } public FluentResults.Result FileExists(string filePath) { ((IService)this).CheckDisposed(); - throw new NotImplementedException(); + return IOExceptionsOperationRunner(nameof(FileExists), filePath, () => + { + var fp = filePath.CleanUpPath(); + fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp); + return System.IO.File.Exists(fp); + }); } public async Task> TryLoadXmlAsync(string filePath, Encoding encoding = null) { - throw new NotImplementedException(); + var r = await TryLoadTextAsync(filePath, encoding); + if (r is { IsSuccess: true, Value: {} value } && !value.IsNullOrWhiteSpace()) + return XDocument.Parse(value); + return FluentResults.Result.Fail(GetGeneralError(nameof(TryLoadXml), filePath)); } public async Task> TryLoadTextAsync(string filePath, Encoding encoding = null) { - throw new NotImplementedException(); + ((IService)this).CheckDisposed(); + return await IOExceptionsOperationRunnerAsync(nameof(TryLoadTextAsync), filePath, async () => + { + var fp = filePath.CleanUpPath(); + fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp); + return await System.IO.File.ReadAllTextAsync(fp); + }); } public async Task> TryLoadBinaryAsync(string filePath) { - throw new NotImplementedException(); - } - - public async Task TrySaveXmlAsync(string filePath, XDocument document, Encoding encoding = null) - { - throw new NotImplementedException(); + ((IService)this).CheckDisposed(); + return await IOExceptionsOperationRunnerAsync(nameof(TryLoadTextAsync), filePath, async () => + { + var fp = filePath.CleanUpPath(); + fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp); + return await System.IO.File.ReadAllBytesAsync(fp); + }); } + public async Task TrySaveXmlAsync(string filePath, XDocument document, Encoding encoding = null) => await TrySaveTextAsync(filePath, document.ToString(), encoding); public async Task TrySaveTextAsync(string filePath, string text, Encoding encoding = null) { - throw new NotImplementedException(); + ((IService)this).CheckDisposed(); + if (text.IsNullOrWhiteSpace()) + { + return FluentResults.Result.Fail($"Contents are empty for {filePath}") + .WithError(new Error($"Contents are empty for {filePath}") + .WithMetadata(MetadataType.ExceptionObject, this) + .WithMetadata(MetadataType.Sources, filePath)); + } + + string t = text.ToString(); //copy + return await IOExceptionsOperationRunnerAsync(nameof(TrySaveText), filePath, async () => + { + var fp = filePath.CleanUpPath(); + fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp); + await System.IO.File.WriteAllTextAsync(fp, t, encoding); + return new FluentResults.Result().WithSuccess($"Saved to file successfully"); + }); } public async Task TrySaveBinaryAsync(string filePath, byte[] bytes) { - throw new NotImplementedException(); + ((IService)this).CheckDisposed(); + if (bytes is null || bytes.Length == 0) + { + return FluentResults.Result.Fail($"Byte array is null or empty for {filePath}") + .WithError(new Error($"Byte array is null or empty for {filePath}") + .WithMetadata(MetadataType.ExceptionObject, this) + .WithMetadata(MetadataType.Sources, filePath)); + } + byte[] b = new byte[bytes.Length]; + System.Buffer.BlockCopy(bytes, 0, b, 0, bytes.Length); + return await IOExceptionsOperationRunnerAsync(nameof(TrySaveBinary), filePath, async () => + { + var fp = filePath.CleanUpPath(); + fp = System.IO.Path.IsPathRooted(fp) ? fp : System.IO.Path.GetFullPath(fp); + await System.IO.File.WriteAllBytesAsync(fp, b); + return new FluentResults.Result().WithSuccess($"Saved to file successfully"); + }); } - + private async Task> IOExceptionsOperationRunnerAsync(string funcName, string filepath, Func>> operation) { try @@ -307,6 +411,50 @@ public class StorageService : IStorageService } } + private async Task IOExceptionsOperationRunnerAsync(string funcName, string filepath, Func> operation) + { + try + { + return await operation?.Invoke()!; + } + catch (ArgumentNullException ane) + { + return ReturnException(ane, filepath).WithError(GetGeneralError(funcName, filepath)); + } + catch (ArgumentException ae) + { + return ReturnException(ae, filepath).WithError(GetGeneralError(funcName, filepath)); + } + catch (PathTooLongException ptle) + { + return ReturnException(ptle, filepath).WithError(GetGeneralError(funcName, filepath)); + } + catch (NotSupportedException nse) + { + return ReturnException(nse, filepath).WithError(GetGeneralError(funcName, filepath)); + } + catch (UnauthorizedAccessException uae) + { + return ReturnException(uae, filepath).WithError(GetGeneralError(funcName, filepath)); + } + catch (DirectoryNotFoundException dnfe) + { + return ReturnException(dnfe, filepath).WithError(GetGeneralError(funcName, filepath)); + } + catch (FileNotFoundException fnfe) + { + return ReturnException(fnfe, filepath).WithError(GetGeneralError(funcName, filepath)); + } + catch (SecurityException se) + { + return ReturnException(se, filepath).WithError(GetGeneralError(funcName, filepath)); + } + catch (IOException ioe) + { + return ReturnException(ioe, filepath).WithError(GetGeneralError(nameof(SaveLocalXml), filepath)); + } + } + private FluentResults.Result IOExceptionsOperationRunner(string funcName, string filepath, Func> operation) { try @@ -360,6 +508,59 @@ public class StorageService : IStorageService } } + private FluentResults.Result IOExceptionsOperationRunner(string funcName, string filepath, Func operation) + { + try + { + return operation?.Invoke(); + } + catch (ArgumentNullException ane) + { + return ReturnException(ane, filepath) + .WithError(GetGeneralError(funcName, filepath)); + } + catch (ArgumentException ae) + { + return ReturnException(ae, filepath) + .WithError(GetGeneralError(funcName, filepath)); + } + catch (PathTooLongException ptle) + { + return ReturnException(ptle, filepath) + .WithError(GetGeneralError(funcName, filepath)); + } + catch (NotSupportedException nse) + { + return ReturnException(nse, filepath) + .WithError(GetGeneralError(funcName, filepath)); + } + catch (UnauthorizedAccessException uae) + { + return ReturnException(uae, filepath) + .WithError(GetGeneralError(funcName, filepath)); + } + catch (DirectoryNotFoundException dnfe) + { + return ReturnException(dnfe, filepath) + .WithError(GetGeneralError(funcName, filepath)); + } + catch (FileNotFoundException fnfe) + { + return ReturnException(fnfe, filepath) + .WithError(GetGeneralError(funcName, filepath)); + } + catch (SecurityException se) + { + return ReturnException(se, filepath) + .WithError(GetGeneralError(funcName, filepath)); + } + catch (IOException ioe) + { + return ReturnException(ioe, filepath) + .WithError(GetGeneralError(nameof(SaveLocalXml), filepath)); + } + } + private Error GetGeneralError(string funcName, string localfp, ContentPackage package) => new Error($"{funcName}: Failed to load local file.") .WithMetadata(MetadataType.ExceptionObject, this) @@ -370,10 +571,27 @@ public class StorageService : IStorageService new Error($"{funcName}: Failed to load local file.") .WithMetadata(MetadataType.ExceptionObject, this) .WithMetadata(MetadataType.Sources, localfp); - - private string GetAbsFromLocal(ContentPackage package, string localFilePath) + + private FluentResults.Result GetAbsFromLocal(ContentPackage package, string localFilePath) { - return System.IO.Path.GetFullPath(System.IO.Path.Combine( + if (Path.IsPathRooted(localFilePath)) + { + return new FluentResults.Result().WithError( + new Error($"The path '{localFilePath}' is a rooted path. Must be relative!") + .WithMetadata(MetadataType.ExceptionObject, this) + .WithMetadata(MetadataType.RootObject, localFilePath)); + } + + if (package is null) + { + return new FluentResults.Result().WithError( + new Error($"{nameof(GetAbsFromPackage)} The package reference for {localFilePath} is null!") + .WithMetadata(MetadataType.ExceptionObject, this) + .WithMetadata(MetadataType.RootObject, localFilePath)); + } + + return new FluentResults.Result().WithSuccess($"Path constructed") + .WithValue( System.IO.Path.GetFullPath(System.IO.Path.Combine( _runLocation, LocalStoragePath.Value, LocalFilePathRule.Value.Replace(_packagePathKeyword, package.Name.IsNullOrWhiteSpace() @@ -381,7 +599,36 @@ public class StorageService : IStorageService ? id.Value.ToString() : "_fallbackFolder" : package.Name), - localFilePath)); + localFilePath))); + } + + private FluentResults.Result GetAbsFromPackage(ContentPackage package, string localFilePath) + { + if (package is null) + { + return new FluentResults.Result().WithError( + new Error($"{nameof(GetAbsFromPackage)} The package reference for {localFilePath} is null!") + .WithMetadata(MetadataType.ExceptionObject, this) + .WithMetadata(MetadataType.RootObject, localFilePath)); + } + + if (localFilePath.IsNullOrWhiteSpace()) + { + return new FluentResults.Result().WithValue(Path.GetFullPath(package.Path.CleanUpPath())); + } + + var path = localFilePath.CleanUpPath(); + + if (Path.IsPathRooted(path)) + { + return new FluentResults.Result().WithError( + new Error($"The path '{localFilePath}' is a rooted path. Must be relative!") + .WithMetadata(MetadataType.ExceptionObject, this) + .WithMetadata(MetadataType.RootObject, localFilePath)); + } + + return new FluentResults.Result().WithSuccess($"Path constructed") + .WithValue(Path.Combine(Path.GetFullPath(package.Path.CleanUpPath()), path)); } private FluentResults.Result ReturnException(TException exception, ContentPackage package) where TException : Exception diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IStorageService.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IStorageService.cs index 1c830ab78..0d132fcdd 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IStorageService.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IStorageService.cs @@ -31,9 +31,9 @@ public interface IStorageService : IService FluentResults.Result LoadPackageBinary(ContentPackage package, string localFilePath); FluentResults.Result LoadPackageText(ContentPackage package, string localFilePath); // collections - FluentResults.Result> LoadPackageXmlFiles(ContentPackage package, ImmutableArray localFilePaths); - FluentResults.Result> LoadPackageBinaryFiles(ContentPackage package, ImmutableArray localFilePaths); - FluentResults.Result> LoadPackageTextFiles(ContentPackage package, ImmutableArray localFilePaths); + ImmutableArray<(string, FluentResults.Result)> LoadPackageXmlFiles(ContentPackage package, ImmutableArray localFilePaths); + ImmutableArray<(string, FluentResults.Result)> LoadPackageBinaryFiles(ContentPackage package, ImmutableArray localFilePaths); + ImmutableArray<(string, FluentResults.Result)> LoadPackageTextFiles(ContentPackage package, ImmutableArray localFilePaths); FluentResults.Result> FindFilesInPackage(ContentPackage package, string localSubfolder, string regexFilter, bool searchRecursively); // async // singles @@ -41,9 +41,9 @@ public interface IStorageService : IService Task> LoadPackageBinaryAsync(ContentPackage package, string localFilePath); Task> LoadPackageTextAsync(ContentPackage package, string localFilePath); // collections - Task>> LoadPackageXmlFilesAsync(ContentPackage package, ImmutableArray localFilePaths); - Task>> LoadPackageBinaryFilesAsync(ContentPackage package, ImmutableArray localFilePaths); - Task>> LoadPackageTextFilesAsync(ContentPackage package, ImmutableArray localFilePaths); + Task)>> LoadPackageXmlFilesAsync(ContentPackage package, ImmutableArray localFilePaths); + Task)>> LoadPackageBinaryFilesAsync(ContentPackage package, ImmutableArray localFilePaths); + Task)>> LoadPackageTextFilesAsync(ContentPackage package, ImmutableArray localFilePaths); #endregion