Skip to content

Commit

Permalink
Merge pull request #583 from Mutagen-Modding/env-asset-provider
Browse files Browse the repository at this point in the history
Env asset provider
  • Loading branch information
Noggog authored Dec 30, 2024
2 parents 435a232 + 0cd907a commit 4fa4b79
Show file tree
Hide file tree
Showing 17 changed files with 474 additions and 341 deletions.
10 changes: 9 additions & 1 deletion Mutagen.Bethesda.Autofac/MutagenModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ protected override void Load(ContainerBuilder builder)
.InNamespacesOf(
typeof(IArchiveReaderProvider),
// typeof(IGetFontConfig),
typeof(IAssetProvider),
typeof(IDataDirectoryLookup),
typeof(IImplicitBaseMasterProvider),
typeof(ILoadOrderWriter),
Expand All @@ -48,5 +47,14 @@ protected override void Load(ContainerBuilder builder)
builder.RegisterType<GameLocatorLookupCache>()
.As<IGameDirectoryLookup>()
.As<IDataDirectoryLookup>();
builder.RegisterType<GameAssetProvider>()
.As<IAssetProvider>()
.AsSelf();
builder.RegisterType<DataDirectoryAssetProvider>()
.AsSelf();
builder.RegisterType<ArchiveAssetProvider>()
.AsSelf();
builder.RegisterType<CachedArchiveListingDetailsProvider>()
.AsImplementedInterfaces();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Mutagen.Bethesda.Inis.DI;
using Mutagen.Bethesda.Installs.DI;
using Mutagen.Bethesda.Plugins;
using Mutagen.Bethesda.Plugins.Order.DI;
using Mutagen.Bethesda.Testing;
using Noggog;
using Noggog.Testing.IO;
Expand Down Expand Up @@ -44,38 +45,23 @@ private GetApplicableArchivePaths GetClass(IFileSystem fs)
var ext = new ArchiveExtensionProvider(gameReleaseInjection);
return new GetApplicableArchivePaths(
fs,
new GetArchiveIniListings(
fs,
new IniPathProvider(
gameReleaseInjection,
new IniPathLookupInjection(MyDocumentsPath))),
new CheckArchiveApplicability(
ext),
new DataDirectoryInjection(BaseFolder),
ext);
ext,
new CachedArchiveListingDetailsProvider(
new LoadOrderListingsInjection(Array.Empty<ModKey>()),
new GetArchiveIniListings(
fs,
new IniPathProvider(
gameReleaseInjection,
new IniPathLookupInjection(MyDocumentsPath)))));
}

#region No ModKey

[Fact]
public void NoModKey_Unordered()
{
var fs = GetFileSystem();
fs.File.WriteAllText(Path.Combine(BaseFolder, MyModBsa), string.Empty);
fs.File.WriteAllText(Path.Combine(BaseFolder, SkyrimBsa), string.Empty);
fs.File.WriteAllText(Path.Combine(BaseFolder, SomeExplicitListingBsa), string.Empty);
var get = GetClass(fs);
var applicable = get.Get(Enumerable.Empty<FileName>())
.ToArray();
applicable.Should().Equal(new FilePath[]
{
Path.Combine(BaseFolder, MyModBsa),
Path.Combine(BaseFolder, SkyrimBsa),
Path.Combine(BaseFolder, SomeExplicitListingBsa),
});
}

[Fact]
public void NoModKey_Ordered()
public void NoModKey()
{
var fs = GetFileSystem();
fs.File.WriteAllText(Path.Combine(BaseFolder, MyModBsa), string.Empty);
Expand All @@ -102,7 +88,7 @@ public void Empty()
{
var fs = GetFileSystem();
var get = GetClass(fs);
get.Get(TestConstants.Skyrim, Enumerable.Empty<FileName>())
get.Get(TestConstants.Skyrim)
.Should().BeEmpty();
}

Expand All @@ -119,41 +105,7 @@ public void NullModKey()
}

[Fact]
public void BaseMod_Unordered()
{
var fs = GetFileSystem();
var get = GetClass(fs);
fs.File.WriteAllText(Path.Combine(BaseFolder, SkyrimBsa), string.Empty);
fs.File.WriteAllText(Path.Combine(BaseFolder, SomeExplicitListingBsa), string.Empty);
fs.File.WriteAllText(Path.Combine(BaseFolder, MyModBsa), string.Empty);
var applicable = get.Get(TestConstants.Skyrim, Enumerable.Empty<FileName>())
.ToArray();
applicable.Should().Equal(new FilePath[]
{
Path.Combine(BaseFolder, SkyrimBsa),
Path.Combine(BaseFolder, SomeExplicitListingBsa)
});
}

[Fact]
public void Typical_Unordered()
{
var fs = GetFileSystem();
var get = GetClass(fs);
fs.File.WriteAllText(Path.Combine(BaseFolder, $"{TestConstants.MasterModKey2.Name}.bsa"), string.Empty);
fs.File.WriteAllText(Path.Combine(BaseFolder, SomeExplicitListingBsa), string.Empty);
fs.File.WriteAllText(Path.Combine(BaseFolder, MyModBsa), string.Empty);
var applicable = get.Get(TestConstants.MasterModKey2, Enumerable.Empty<FileName>())
.ToArray();
applicable.Should().Equal(new FilePath[]
{
Path.Combine(BaseFolder, $"{TestConstants.MasterModKey2.Name}.bsa"),
Path.Combine(BaseFolder, SomeExplicitListingBsa)
});
}

[Fact]
public void BaseMod_Ordered()
public void BaseMod()
{
var fs = GetFileSystem();
var get = GetClass(fs);
Expand All @@ -170,7 +122,7 @@ public void BaseMod_Ordered()
}

[Fact]
public void Typical_Ordered()
public void Typical()
{
var fs = GetFileSystem();
var get = GetClass(fs);
Expand All @@ -182,7 +134,7 @@ public void Typical_Ordered()
applicable.Should().Equal(new FilePath[]
{
Path.Combine(BaseFolder, SomeExplicitListingBsa),
Path.Combine(BaseFolder, $"{TestConstants.MasterModKey2.Name}.bsa"),
Path.Combine(BaseFolder, $"{TestConstants.MasterModKey2.Name}.bsa")
});
}
#endregion
Expand Down
68 changes: 26 additions & 42 deletions Mutagen.Bethesda.Core/Archives/Archive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using Mutagen.Bethesda.Environments.DI;
using Mutagen.Bethesda.Inis.DI;
using Mutagen.Bethesda.Installs.DI;
using Mutagen.Bethesda.Plugins.Order;
using Mutagen.Bethesda.Plugins.Order.DI;
using Stream = System.IO.Stream;

namespace Mutagen.Bethesda.Archives;
Expand All @@ -14,23 +16,28 @@ public static class Archive
private static GetApplicableArchivePaths GetApplicableArchivePathsDi(
GameRelease release,
DirectoryPath dataFolderPath,
IFileSystem? fileSystem = null)
IEnumerable<ModKey>? modOrdering,
IFileSystem? fileSystem)
{
fileSystem ??= fileSystem.GetOrDefault();
var gameReleaseInjection = new GameReleaseInjection(release);
var ext = new ArchiveExtensionProvider(gameReleaseInjection);
var lo = new LoadOrderListingsInjection(
modOrdering.EmptyIfNull().Select(x => new LoadOrderListing(x, enabled: true)));
return new GetApplicableArchivePaths(
fileSystem,
new GetArchiveIniListings(
fileSystem,
new IniPathProvider(
new GameReleaseInjection(release),
new IniPathLookup(
GameLocatorLookupCache.Instance))),
new CheckArchiveApplicability(
ext),
new DataDirectoryInjection(dataFolderPath),
ext);
ext,
new CachedArchiveListingDetailsProvider(
lo,
new GetArchiveIniListings(
fileSystem,
new IniPathProvider(
new GameReleaseInjection(release),
new IniPathLookup(
GameLocatorLookupCache.Instance)))));
}

/// <summary>
Expand Down Expand Up @@ -78,8 +85,8 @@ public static IEnumerable<FilePath> GetApplicableArchivePaths(
GameRelease release, DirectoryPath dataFolderPath, IFileSystem? fileSystem = null,
bool returnEmptyIfMissing = true)
{
return GetApplicableArchivePathsDi(release, dataFolderPath, fileSystem)
.Get(returnEmptyIfMissing: returnEmptyIfMissing);
return GetApplicableArchivePathsDi(release, dataFolderPath, modOrdering: null, fileSystem: fileSystem)
.Get();
}

/// <summary>
Expand All @@ -89,13 +96,12 @@ public static IEnumerable<FilePath> GetApplicableArchivePaths(
/// <param name="dataFolderPath">Folder to query within</param>
/// <param name="archiveOrdering">Archive ordering overload. Empty enumerable means no ordering.</param>
/// <param name="fileSystem">FileSystem to use</param>
/// <param name="returnEmptyIfMissing">If ini file is missing, return empty instead of throwing an exception</param>
/// <returns></returns>
public static IEnumerable<FilePath> GetApplicableArchivePaths(GameRelease release, DirectoryPath dataFolderPath,
IEnumerable<FileName>? archiveOrdering, IFileSystem? fileSystem = null, bool returnEmptyIfMissing = true)
{
return GetApplicableArchivePathsDi(release, dataFolderPath, fileSystem)
.Get(archiveOrdering, returnEmptyIfMissing: returnEmptyIfMissing);
return GetApplicableArchivePathsDi(release, dataFolderPath, archiveOrdering.EmptyIfNull().Select(ModKey.FromFileName), fileSystem)
.Get();
}

/// <summary>
Expand All @@ -105,13 +111,12 @@ public static IEnumerable<FilePath> GetApplicableArchivePaths(GameRelease releas
/// <param name="dataFolderPath">Folder to query within</param>
/// <param name="modOrdering">Archive ordering overload based on a mod order. Empty enumerable means no ordering.</param>
/// <param name="fileSystem">FileSystem to use</param>
/// <param name="returnEmptyIfMissing">If ini file is missing, return empty instead of throwing an exception</param>
/// <returns></returns>
public static IEnumerable<FilePath> GetApplicableArchivePaths(GameRelease release, DirectoryPath dataFolderPath,
IEnumerable<ModKey>? modOrdering, IFileSystem? fileSystem = null, bool returnEmptyIfMissing = true)
{
return GetApplicableArchivePathsDi(release, dataFolderPath, fileSystem)
.Get(modOrdering, returnEmptyIfMissing: returnEmptyIfMissing);
return GetApplicableArchivePathsDi(release, dataFolderPath, modOrdering, fileSystem)
.Get();
}

/// <summary>
Expand All @@ -123,13 +128,12 @@ public static IEnumerable<FilePath> GetApplicableArchivePaths(GameRelease releas
/// <param name="dataFolderPath">Folder to query within</param>
/// <param name="modKey">ModKey to query about</param>
/// <param name="fileSystem">FileSystem to use</param>
/// <param name="returnEmptyIfMissing">If ini file is missing, return empty instead of throwing an exception</param>
/// <returns></returns>
public static IEnumerable<FilePath> GetApplicableArchivePaths(GameRelease release, DirectoryPath dataFolderPath,
ModKey modKey, IFileSystem? fileSystem = null, bool returnEmptyIfMissing = true)
{
return GetApplicableArchivePathsDi(release, dataFolderPath, fileSystem)
.Get(modKey, returnEmptyIfMissing: returnEmptyIfMissing);
return GetApplicableArchivePathsDi(release, dataFolderPath, modOrdering: null, fileSystem: fileSystem)
.Get(modKey);
}

/// <summary>
Expand All @@ -142,33 +146,13 @@ public static IEnumerable<FilePath> GetApplicableArchivePaths(GameRelease releas
/// <param name="modKey">ModKey to query about</param>
/// <param name="archiveOrdering">Archive ordering overload. Empty enumerable means no ordering.</param>
/// <param name="fileSystem">FileSystem to use</param>
/// <param name="returnEmptyIfMissing">If ini file is missing, return empty instead of throwing an exception</param>
/// <returns></returns>
public static IEnumerable<FilePath> GetApplicableArchivePaths(GameRelease release, DirectoryPath dataFolderPath,
ModKey modKey, IEnumerable<FileName>? archiveOrdering,
IFileSystem? fileSystem = null, bool returnEmptyIfMissing = true)
{
return GetApplicableArchivePathsDi(release, dataFolderPath, fileSystem)
.Get(modKey, archiveOrdering, returnEmptyIfMissing: returnEmptyIfMissing);
}

/// <summary>
/// Enumerates all applicable Archives for a given release and ModKey that are within a given dataFolderPath.<br/>
/// This call is intended to return Archives related to one specific mod.<br/>
/// NOTE: It is currently a bit experimental
/// </summary>
/// <param name="release">GameRelease to query for</param>
/// <param name="dataFolderPath">Folder to query within</param>
/// <param name="modKey">ModKey to query about</param>
/// <param name="archiveOrdering">How to order the archive paths. Null for no ordering</param>
/// <param name="fileSystem">FileSystem to use</param>
/// <param name="returnEmptyIfMissing">If ini file is missing, return empty instead of throwing an exception</param>
/// <returns>Full paths of Archives that apply to the given mod and exist</returns>
public static IEnumerable<FilePath> GetApplicableArchivePaths(GameRelease release, DirectoryPath dataFolderPath,
ModKey modKey, IComparer<FileName>? archiveOrdering, IFileSystem? fileSystem = null, bool returnEmptyIfMissing = true)
IFileSystem? fileSystem = null)
{
return GetApplicableArchivePathsDi(release, dataFolderPath, fileSystem)
.Get(modKey, archiveOrdering, returnEmptyIfMissing: returnEmptyIfMissing);
return GetApplicableArchivePathsDi(release, dataFolderPath, archiveOrdering.EmptyIfNull().Select(ModKey.FromFileName), fileSystem)
.Get(modKey);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Mutagen.Bethesda.Plugins.Order.DI;
using Noggog;

namespace Mutagen.Bethesda.Archives.DI;

public class CachedArchiveListingDetailsProvider : IArchiveListingDetailsProvider
{
private readonly ILoadOrderListingsProvider _listingsProvider;
private readonly IGetArchiveIniListings _getArchiveIniListings;
private readonly Lazy<Payload> _payload;

private class Payload
{
public required IReadOnlyList<FileName> Listed { get; init; }
public required IReadOnlyList<FileName> Priority { get; init; }
public required IReadOnlySet<FileName> Set { get; init; }
}

public CachedArchiveListingDetailsProvider(
ILoadOrderListingsProvider listingsProvider,
IGetArchiveIniListings getArchiveIniListings)
{
_listingsProvider = listingsProvider;
_getArchiveIniListings = getArchiveIniListings;
_payload = new Lazy<Payload>(() =>
{
var listed = new List<FileName>();
listed.AddRange(_getArchiveIniListings.TryGet().EmptyIfNull());
listed.AddRange(_listingsProvider.Get()
.Where(x => x.Enabled)
.Select(x => x.ModKey)
.Select(x => x.FileName));
return new Payload()
{
Listed = listed,
Priority = ((IEnumerable<FileName>)listed).Reverse().ToList(),
Set = listed.ToHashSet(),
};
});
}

public bool Empty => _payload.Value.Listed.Count == 0;

public int PriorityIndexFor(FileName fileName)
{
return _payload.Value.Priority.IndexOf(fileName);
}

public bool Contains(FileName fileName)
{
return _payload.Value.Set.Contains(fileName);
}
}
Loading

0 comments on commit 4fa4b79

Please sign in to comment.