Skip to content

Commit

Permalink
Merge pull request #1799 from Nexus-Mods/update-cyberpunk-installers
Browse files Browse the repository at this point in the history
Update cyberpunk installers to new LoadoutItem format
  • Loading branch information
erri120 authored Jul 24, 2024
2 parents 1a05f33 + 7024994 commit 39340bb
Show file tree
Hide file tree
Showing 34 changed files with 3,318 additions and 76 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using NexusMods.Abstractions.FileStore.Trees;
using NexusMods.Abstractions.GameLocators;
using NexusMods.Abstractions.Library.Models;
using NexusMods.Abstractions.Loadouts;
using NexusMods.Abstractions.Loadouts.Files;
using NexusMods.MnemonicDB.Abstractions;
using NexusMods.MnemonicDB.Abstractions.Models;
using NexusMods.Paths;
using NexusMods.Paths.Trees;
Expand All @@ -20,7 +23,7 @@ public static TempEntity ToStoredFile(this KeyedBox<RelativePath, ModFileTree> i
{
return input.ToStoredFile(to, null);
}

/// <summary>
/// Creates a StoredFile from a ModFileTreeSource.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using DynamicData.Kernel;
using JetBrains.Annotations;
using NexusMods.Abstractions.GameLocators;
using NexusMods.Abstractions.Library.Models;
using NexusMods.Abstractions.Loadouts;
using NexusMods.MnemonicDB.Abstractions;
using NexusMods.Paths;
using NexusMods.Paths.Trees;

namespace NexusMods.Abstractions.Library.Installers;

Expand Down Expand Up @@ -33,4 +36,36 @@ public static LoadoutItemGroup.New ToGroup(

return group;
}

/// <summary>
/// Convert as LibraryArchiveTree node to a LoadoutFile with the given metadata
/// </summary>
public static LoadoutFile.New ToLoadoutFile(
this KeyedBox<RelativePath, LibraryArchiveTree> input,
LoadoutId loadoutId,
LoadoutItemGroupId parent,
ITransaction tx,
GamePath to,
Optional<EntityId> entityId = default)
{
var id = entityId.ValueOr(tx.TempId);
var libraryFile = input.Item.LibraryFile.Value;

return new LoadoutFile.New(tx, id)
{
LoadoutItemWithTargetPath = new LoadoutItemWithTargetPath.New(tx, id)
{
LoadoutItem = new LoadoutItem.New(tx, id)
{
LoadoutId = loadoutId,
IsIsDisabledMarker = false,
Name = input.Item.Value.FileName,
ParentId = parent,
},
TargetPath = to,
},
Hash = libraryFile.Hash,
Size = libraryFile.Size,
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using DynamicData.Kernel;
using NexusMods.Paths;
using NexusMods.Paths.Trees;
using NexusMods.Paths.Trees.Traits;

namespace NexusMods.Abstractions.Library.Models;
using LibraryArchiveTreeNode = NexusMods.Paths.Trees.KeyedBox<NexusMods.Paths.RelativePath, LibraryArchiveTree>;

/// <summary>
/// Represents a tree of files sourced from a downloaded mod.
/// </summary>
/// <remarks>
/// See this for reference https://github.com/Nexus-Mods/NexusMods.Paths/blob/main/docs/Trees/Introduction.md
/// </remarks>
public struct LibraryArchiveTree :
IHaveBoxedChildrenWithKey<RelativePath, LibraryArchiveTree>, // basic functionality
IHaveAFileOrDirectory, // for uses which want to distinguish file/directory
IHaveParent<LibraryArchiveTree>, // optimized FindSubPathsByKeyUpward
IHaveDepthInformation, // Depth info is used by some installers, and it's zero-cost to include, thanks to leftover padding space
IHavePathSegment, // optimized path segment based operations
IHaveKey<RelativePath>,
IHaveValue<LibraryArchiveTree>
{
/// <inheritdoc />
public Box<LibraryArchiveTree>? Parent { get; private set; } // 0

/// <inheritdoc />
public Dictionary<RelativePath, LibraryArchiveTreeNode> Children { get; private set; } // 8

/// <inheritdoc />
public ushort Depth { get; private set; } // 17

/// <summary>
/// True if this node represents a file.
/// </summary>
public bool IsFile => LibraryFile.HasValue;

/// <inheritdoc />
public RelativePath Segment { get; init; } // 24

/// <summary>
/// The library file, this node represents.
/// </summary>
public Optional<LibraryFile.ReadOnly> LibraryFile { get; init; }

/// <summary>
/// The complete path of the file or directory.
/// </summary>
public RelativePath Path => this.ReconstructPath();

/// <summary>
/// The name file or directory in this node.
/// </summary>
public RelativePath FileName => Segment;

// Interface Redirects
/// <inheritdoc />
public RelativePath Key => Segment;

/// <inheritdoc />
public LibraryArchiveTree Value => this;

/// <summary>
/// Creates the tree! From the source entries.
/// </summary>
public static LibraryArchiveTreeNode Create(LibraryArchive.ReadOnly archive)
{
// Unboxed root node.
var root = CreateDirectoryNode(RelativePath.Empty, 0);

// Add each entry to the tree.
foreach (var entry in archive.Children)
{
var libraryFile = entry.AsLibraryFile();
var current = root;
var parts = entry.Path.GetParts();

for (var x = 0; x < parts.Length; x++)
{
var segment = parts[x];
var isFile = x == parts.Length - 1;

// Try get child for this segment.
if (!current.Item.Children.TryGetValue(segment, out var child))
{
var depth = (ushort)(x + 1);
child = isFile ? CreateFileNode(segment, libraryFile, depth, current) : CreateDirectoryNode(segment, depth, current);
current.Item.Children.Add(segment, child);
}

current = child;
}
}

return root;
}

private static LibraryArchiveTreeNode CreateDirectoryNode(RelativePath segmentName, ushort depth, Box<LibraryArchiveTree>? parent = null)
=> CreateFileNode(segmentName, Optional<LibraryFile.ReadOnly>.None, depth, parent);

private static LibraryArchiveTreeNode CreateFileNode(RelativePath segmentName, Optional<LibraryFile.ReadOnly> libraryFile, ushort depth, Box<LibraryArchiveTree>? parent)
{
return new LibraryArchiveTreeNode
{
Item = new LibraryArchiveTree
{
Segment = segmentName,
Children = new Dictionary<RelativePath, LibraryArchiveTreeNode>(),
Parent = parent,
LibraryFile = libraryFile,
Depth = depth,
},
};
}

}

/// <summary>
/// Converters and extensions for <see cref="LibraryArchiveTree"/>.
/// </summary>
public static class LibraryArchiveTreeExtensions
{
/// <summary>
/// Organize the children of this node into a tree based on their paths.
/// </summary>
public static LibraryArchiveTreeNode GetTree(this LibraryArchive.ReadOnly archive)
=> LibraryArchiveTree.Create(archive);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@
<Compile Update="LibraryArchiveFileEntry.cs">
<DependentUpon>LibraryArchive.cs</DependentUpon>
</Compile>
<Compile Update="LibraryArchiveTree.cs">
<DependentUpon>LibraryArchive.cs</DependentUpon>
</Compile>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using NexusMods.Abstractions.Installers;
using NexusMods.Abstractions.IO;
using NexusMods.Abstractions.IO.StreamFactories;
using NexusMods.Abstractions.Library.Installers;
using NexusMods.Abstractions.Loadouts.Synchronizers;
using NexusMods.Games.FOMOD;
using NexusMods.Games.RedEngine.Cyberpunk2077.Emitters;
Expand Down Expand Up @@ -77,12 +78,21 @@ protected override IReadOnlyDictionary<LocationId, AbsolutePath> GetLocations(IF
/// <inheritdoc />
public override IEnumerable<IModInstaller> Installers => new IModInstaller[]
{
new RedModInstaller(),
new SimpleOverlayModInstaller(),
new AppearancePreset(_serviceProvider),
new FolderlessModInstaller(),
new RedModInstaller(_serviceProvider),
new SimpleOverlayModInstaller(_serviceProvider),
new AppearancePresetInstaller(_serviceProvider),
new FolderlessModInstaller(_serviceProvider),
FomodXmlInstaller.Create(_serviceProvider, new GamePath(LocationId.Game, "/")),
};

/// <inheritdoc />
public override ILibraryItemInstaller[] LibraryItemInstallers =>
[
new RedModInstaller(_serviceProvider),
new SimpleOverlayModInstaller(_serviceProvider),
new AppearancePresetInstaller(_serviceProvider),
new FolderlessModInstaller(_serviceProvider),
];

public override List<IModInstallDestination> GetInstallDestinations(IReadOnlyDictionary<LocationId, AbsolutePath> locations)
=> ModInstallDestinationHelpers.GetCommonLocations(locations);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using NexusMods.Abstractions.Library.Models;
using NexusMods.Abstractions.Loadouts;
using NexusMods.MnemonicDB.Abstractions.Attributes;
using NexusMods.MnemonicDB.Abstractions.Models;

namespace NexusMods.Games.RedEngine.Cyberpunk2077.Models;


[Include<LoadoutFile>]
public partial class RedModInfoFile : IModelDefinition
{
private static string Namespace => "NexusMods.Games.RedEngine.Cyberpunk2077.RedModInfoFile";

/// <summary>
/// The internal name of the mod
/// </summary>
public static readonly StringAttribute Name = new(Namespace, nameof(Name));

/// <summary>
/// The internal version of the mod
/// </summary>
public static readonly StringAttribute Version = new(Namespace, nameof(Version));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using NexusMods.Abstractions.Library.Models;
using NexusMods.Abstractions.Loadouts;
using NexusMods.MnemonicDB.Abstractions.Attributes;
using NexusMods.MnemonicDB.Abstractions.Models;

namespace NexusMods.Games.RedEngine.Cyberpunk2077.Models;

[Include<LoadoutItemGroup>]
public partial class RedModLoadoutGroup : IModelDefinition
{
private const string Namespace = "NexusMods.Games.RedEngine.Cyberpunk2077.RedModLoadoutGroup";

/// <summary>
/// The info.json file for this RedMod
/// </summary>
public static readonly ReferenceAttribute<RedModInfoFile> RedModInfoFile = new(Namespace, nameof(RedModInfoFile));
}

This file was deleted.

Loading

0 comments on commit 39340bb

Please sign in to comment.