Penumbra/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs
2023-12-20 16:39:26 +01:00

170 lines
6 KiB
C#

using Penumbra.Collections;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Meta.Manipulations;
using Penumbra.String.Classes;
using Penumbra.Meta;
using Penumbra.Mods.Manager;
using Penumbra.Mods.Subclasses;
namespace Penumbra.Mods.ItemSwap;
public class ItemSwapContainer
{
private readonly MetaFileManager _manager;
private readonly ObjectIdentification _identifier;
private Dictionary<Utf8GamePath, FullPath> _modRedirections = [];
private HashSet<MetaManipulation> _modManipulations = [];
public IReadOnlyDictionary<Utf8GamePath, FullPath> ModRedirections
=> _modRedirections;
public IReadOnlySet<MetaManipulation> ModManipulations
=> _modManipulations;
public readonly List<Swap> Swaps = [];
public bool Loaded { get; private set; }
public void Clear()
{
Swaps.Clear();
Loaded = false;
}
public enum WriteType
{
UseSwaps,
NoSwaps,
}
public bool WriteMod(ModManager manager, Mod mod, WriteType writeType = WriteType.NoSwaps, DirectoryInfo? directory = null,
int groupIndex = -1, int optionIndex = 0)
{
var convertedManips = new HashSet<MetaManipulation>(Swaps.Count);
var convertedFiles = new Dictionary<Utf8GamePath, FullPath>(Swaps.Count);
var convertedSwaps = new Dictionary<Utf8GamePath, FullPath>(Swaps.Count);
directory ??= mod.ModPath;
try
{
foreach (var swap in Swaps.SelectMany(s => s.WithChildren()))
{
switch (swap)
{
case FileSwap file:
// Skip, nothing to do
if (file.SwapToModdedEqualsOriginal)
continue;
if (writeType == WriteType.UseSwaps && file.SwapToModdedExistsInGame && !file.DataWasChanged)
{
convertedSwaps.TryAdd(file.SwapFromRequestPath, file.SwapToModded);
}
else
{
var path = file.GetNewPath(directory.FullName);
var bytes = file.FileData.Write();
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
_manager.Compactor.WriteAllBytes(path, bytes);
convertedFiles.TryAdd(file.SwapFromRequestPath, new FullPath(path));
}
break;
case MetaSwap meta:
if (!meta.SwapAppliedIsDefault)
convertedManips.Add(meta.SwapApplied);
break;
}
}
manager.OptionEditor.OptionSetFiles(mod, groupIndex, optionIndex, convertedFiles);
manager.OptionEditor.OptionSetFileSwaps(mod, groupIndex, optionIndex, convertedSwaps);
manager.OptionEditor.OptionSetManipulations(mod, groupIndex, optionIndex, convertedManips);
return true;
}
catch (Exception e)
{
Penumbra.Log.Error($"Could not write FileSwapContainer to {mod.ModPath}:\n{e}");
return false;
}
}
public void LoadMod(Mod? mod, ModSettings? settings)
{
Clear();
if (mod == null)
{
_modRedirections = new Dictionary<Utf8GamePath, FullPath>();
_modManipulations = new HashSet<MetaManipulation>();
}
else
{
(_modRedirections, _modManipulations) = ModSettings.GetResolveData(mod, settings);
}
}
public ItemSwapContainer(MetaFileManager manager, ObjectIdentification identifier)
{
_manager = manager;
_identifier = identifier;
LoadMod(null, null);
}
private Func<Utf8GamePath, FullPath> PathResolver(ModCollection? collection)
=> collection != null
? p => collection.ResolvePath(p) ?? new FullPath(p)
: p => ModRedirections.TryGetValue(p, out var path) ? path : new FullPath(p);
private Func<MetaManipulation, MetaManipulation> MetaResolver(ModCollection? collection)
{
var set = collection?.MetaCache?.Manipulations.ToHashSet() ?? _modManipulations;
return m => set.TryGetValue(m, out var a) ? a : m;
}
public EquipItem[] LoadEquipment(EquipItem from, EquipItem to, ModCollection? collection = null, bool useRightRing = true,
bool useLeftRing = true)
{
Swaps.Clear();
Loaded = false;
var ret = EquipmentSwap.CreateItemSwap(_manager, _identifier, Swaps, PathResolver(collection), MetaResolver(collection),
from, to, useRightRing, useLeftRing);
Loaded = true;
return ret;
}
public EquipItem[] LoadTypeSwap(EquipSlot slotFrom, EquipItem from, EquipSlot slotTo, EquipItem to, ModCollection? collection = null)
{
Swaps.Clear();
Loaded = false;
var ret = EquipmentSwap.CreateTypeSwap(_manager, _identifier, Swaps, PathResolver(collection), MetaResolver(collection),
slotFrom, from, slotTo, to);
Loaded = true;
return ret;
}
public bool LoadCustomization(MetaFileManager manager, BodySlot slot, GenderRace race, PrimaryId from, PrimaryId to,
ModCollection? collection = null)
{
var pathResolver = PathResolver(collection);
var mdl = CustomizationSwap.CreateMdl(manager, pathResolver, slot, race, from, to);
var type = slot switch
{
BodySlot.Hair => EstManipulation.EstType.Hair,
BodySlot.Face => EstManipulation.EstType.Face,
_ => (EstManipulation.EstType)0,
};
var metaResolver = MetaResolver(collection);
var est = ItemSwap.CreateEst(manager, pathResolver, metaResolver, type, race, from, to, true);
Swaps.Add(mdl);
if (est != null)
Swaps.Add(est);
Loaded = true;
return true;
}
}