mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Add some functionality to allow an IMC group to add apply to all variants.
This commit is contained in:
parent
137b752196
commit
05d010a281
10 changed files with 117 additions and 46 deletions
|
|
@ -51,7 +51,7 @@ public readonly struct ImcCache : IDisposable
|
|||
try
|
||||
{
|
||||
if (!_imcFiles.TryGetValue(path, out var file))
|
||||
file = new ImcFile(manager, manip);
|
||||
file = new ImcFile(manager, manip.Identifier);
|
||||
|
||||
_imcManipulations[idx] = (manip, file);
|
||||
if (!manip.Apply(file))
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ public partial class TexToolsMeta
|
|||
var manip = new ImcManipulation(metaFileInfo.PrimaryType, metaFileInfo.SecondaryType, metaFileInfo.PrimaryId,
|
||||
metaFileInfo.SecondaryId, i, metaFileInfo.EquipSlot,
|
||||
new ImcEntry());
|
||||
var def = new ImcFile(_metaFileManager, manip);
|
||||
var def = new ImcFile(_metaFileManager, manip.Identifier);
|
||||
var partIdx = ImcFile.PartIndex(manip.EquipSlot); // Gets turned to unknown for things without equip, and unknown turns to 0.
|
||||
foreach (var value in values)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ public partial class TexToolsMeta
|
|||
{
|
||||
case MetaManipulation.Type.Imc:
|
||||
var allManips = manips.ToList();
|
||||
var baseFile = new ImcFile(manager, allManips[0].Imc);
|
||||
var baseFile = new ImcFile(manager, allManips[0].Imc.Identifier);
|
||||
foreach (var manip in allManips)
|
||||
manip.Imc.Apply(baseFile);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,20 +9,20 @@ namespace Penumbra.Meta.Files;
|
|||
|
||||
public class ImcException : Exception
|
||||
{
|
||||
public readonly ImcManipulation Manipulation;
|
||||
public readonly string GamePath;
|
||||
public readonly ImcIdentifier Identifier;
|
||||
public readonly string GamePath;
|
||||
|
||||
public ImcException(ImcManipulation manip, Utf8GamePath path)
|
||||
public ImcException(ImcIdentifier identifier, Utf8GamePath path)
|
||||
{
|
||||
Manipulation = manip;
|
||||
GamePath = path.ToString();
|
||||
Identifier = identifier;
|
||||
GamePath = path.ToString();
|
||||
}
|
||||
|
||||
public override string Message
|
||||
=> "Could not obtain default Imc File.\n"
|
||||
+ " Either the default file does not exist (possibly for offhand files from TexTools) or the installation is corrupted.\n"
|
||||
+ $" Game Path: {GamePath}\n"
|
||||
+ $" Manipulation: {Manipulation}";
|
||||
+ $" Manipulation: {Identifier}";
|
||||
}
|
||||
|
||||
public unsafe class ImcFile : MetaBaseFile
|
||||
|
|
@ -142,13 +142,14 @@ public unsafe class ImcFile : MetaBaseFile
|
|||
}
|
||||
}
|
||||
|
||||
public ImcFile(MetaFileManager manager, ImcManipulation manip)
|
||||
public ImcFile(MetaFileManager manager, ImcIdentifier identifier)
|
||||
: base(manager, 0)
|
||||
{
|
||||
Path = manip.GamePath();
|
||||
var file = manager.GameData.GetFile(Path.ToString());
|
||||
var path = identifier.GamePathString();
|
||||
Path = Utf8GamePath.FromString(path, out var p) ? p : Utf8GamePath.Empty;
|
||||
var file = manager.GameData.GetFile(path);
|
||||
if (file == null)
|
||||
throw new ImcException(manip, Path);
|
||||
throw new ImcException(identifier, Path);
|
||||
|
||||
fixed (byte* ptr = file.Data)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,11 +4,32 @@ using Penumbra.Meta.Manipulations;
|
|||
|
||||
namespace Penumbra.Meta;
|
||||
|
||||
public class ImcChecker(MetaFileManager metaFileManager)
|
||||
public class ImcChecker
|
||||
{
|
||||
private static readonly Dictionary<ImcIdentifier, int> VariantCounts = [];
|
||||
private static MetaFileManager? _dataManager;
|
||||
|
||||
|
||||
public static int GetVariantCount(ImcIdentifier identifier)
|
||||
{
|
||||
if (VariantCounts.TryGetValue(identifier, out var count))
|
||||
return count;
|
||||
|
||||
count = GetFile(identifier)?.Count ?? 0;
|
||||
VariantCounts[identifier] = count;
|
||||
return count;
|
||||
}
|
||||
|
||||
public readonly record struct CachedEntry(ImcEntry Entry, bool FileExists, bool VariantExists);
|
||||
|
||||
private readonly Dictionary<ImcIdentifier, CachedEntry> _cachedDefaultEntries = new();
|
||||
private readonly MetaFileManager _metaFileManager;
|
||||
|
||||
public ImcChecker(MetaFileManager metaFileManager)
|
||||
{
|
||||
_metaFileManager = metaFileManager;
|
||||
_dataManager = metaFileManager;
|
||||
}
|
||||
|
||||
public CachedEntry GetDefaultEntry(ImcIdentifier identifier, bool storeCache)
|
||||
{
|
||||
|
|
@ -17,7 +38,7 @@ public class ImcChecker(MetaFileManager metaFileManager)
|
|||
|
||||
try
|
||||
{
|
||||
var e = ImcFile.GetDefault(metaFileManager, identifier.GamePath(), identifier.EquipSlot, identifier.Variant, out var entryExists);
|
||||
var e = ImcFile.GetDefault(_metaFileManager, identifier.GamePath(), identifier.EquipSlot, identifier.Variant, out var entryExists);
|
||||
entry = new CachedEntry(e, true, entryExists);
|
||||
}
|
||||
catch (Exception)
|
||||
|
|
@ -33,4 +54,19 @@ public class ImcChecker(MetaFileManager metaFileManager)
|
|||
public CachedEntry GetDefaultEntry(ImcManipulation imcManip, bool storeCache)
|
||||
=> GetDefaultEntry(new ImcIdentifier(imcManip.PrimaryId, imcManip.Variant, imcManip.ObjectType, imcManip.SecondaryId.Id,
|
||||
imcManip.EquipSlot, imcManip.BodySlot), storeCache);
|
||||
|
||||
private static ImcFile? GetFile(ImcIdentifier identifier)
|
||||
{
|
||||
if (_dataManager == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return new ImcFile(_dataManager, identifier);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,12 +31,16 @@ public readonly record struct ImcIdentifier(
|
|||
=> new(ObjectType, BodySlot, PrimaryId, SecondaryId.Id, Variant.Id, EquipSlot, entry);
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, object?> changedItems)
|
||||
=> AddChangedItems(identifier, changedItems, false);
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, object?> changedItems, bool allVariants)
|
||||
{
|
||||
var path = ObjectType switch
|
||||
{
|
||||
ObjectType.Equipment or ObjectType.Accessory => GamePaths.Equipment.Mtrl.Path(PrimaryId, GenderRace.MidlanderMale, EquipSlot,
|
||||
Variant,
|
||||
"a"),
|
||||
ObjectType.Equipment when allVariants => GamePaths.Equipment.Mdl.Path(PrimaryId, GenderRace.MidlanderMale, EquipSlot),
|
||||
ObjectType.Equipment => GamePaths.Equipment.Mtrl.Path(PrimaryId, GenderRace.MidlanderMale, EquipSlot, Variant, "a"),
|
||||
ObjectType.Accessory when allVariants => GamePaths.Accessory.Mdl.Path(PrimaryId, GenderRace.MidlanderMale, EquipSlot),
|
||||
ObjectType.Accessory => GamePaths.Accessory.Mtrl.Path(PrimaryId, GenderRace.MidlanderMale, EquipSlot, Variant, "a"),
|
||||
ObjectType.Weapon => GamePaths.Weapon.Mtrl.Path(PrimaryId, SecondaryId.Id, Variant, "a"),
|
||||
ObjectType.DemiHuman => GamePaths.DemiHuman.Mtrl.Path(PrimaryId, SecondaryId.Id, EquipSlot, Variant,
|
||||
"a"),
|
||||
|
|
@ -49,24 +53,19 @@ public readonly record struct ImcIdentifier(
|
|||
identifier.Identify(changedItems, path);
|
||||
}
|
||||
|
||||
public Utf8GamePath GamePath()
|
||||
{
|
||||
return ObjectType switch
|
||||
public string GamePathString()
|
||||
=> ObjectType switch
|
||||
{
|
||||
ObjectType.Accessory => Utf8GamePath.FromString(GamePaths.Accessory.Imc.Path(PrimaryId), out var p) ? p : Utf8GamePath.Empty,
|
||||
ObjectType.Equipment => Utf8GamePath.FromString(GamePaths.Equipment.Imc.Path(PrimaryId), out var p) ? p : Utf8GamePath.Empty,
|
||||
ObjectType.DemiHuman => Utf8GamePath.FromString(GamePaths.DemiHuman.Imc.Path(PrimaryId, SecondaryId.Id), out var p)
|
||||
? p
|
||||
: Utf8GamePath.Empty,
|
||||
ObjectType.Monster => Utf8GamePath.FromString(GamePaths.Monster.Imc.Path(PrimaryId, SecondaryId.Id), out var p)
|
||||
? p
|
||||
: Utf8GamePath.Empty,
|
||||
ObjectType.Weapon => Utf8GamePath.FromString(GamePaths.Weapon.Imc.Path(PrimaryId, SecondaryId.Id), out var p)
|
||||
? p
|
||||
: Utf8GamePath.Empty,
|
||||
_ => throw new NotImplementedException(),
|
||||
ObjectType.Accessory => GamePaths.Accessory.Imc.Path(PrimaryId),
|
||||
ObjectType.Equipment => GamePaths.Equipment.Imc.Path(PrimaryId),
|
||||
ObjectType.DemiHuman => GamePaths.DemiHuman.Imc.Path(PrimaryId, SecondaryId.Id),
|
||||
ObjectType.Monster => GamePaths.Monster.Imc.Path(PrimaryId, SecondaryId.Id),
|
||||
ObjectType.Weapon => GamePaths.Weapon.Imc.Path(PrimaryId, SecondaryId.Id),
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
public Utf8GamePath GamePath()
|
||||
=> Utf8GamePath.FromString(GamePathString(), out var p) ? p : Utf8GamePath.Empty;
|
||||
|
||||
public MetaIndex FileIndex()
|
||||
=> (MetaIndex)(-1);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using OtterGui.Classes;
|
|||
using Penumbra.Api.Enums;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods.Settings;
|
||||
using Penumbra.Mods.SubMods;
|
||||
|
|
@ -31,6 +32,8 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
|
||||
public ImcIdentifier Identifier;
|
||||
public ImcEntry DefaultEntry;
|
||||
public bool AllVariants;
|
||||
|
||||
|
||||
public FullPath? FindBestMatch(Utf8GamePath gamePath)
|
||||
=> null;
|
||||
|
|
@ -39,7 +42,7 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
|
||||
public bool CanBeDisabled
|
||||
{
|
||||
get => OptionData.Any(m => m.IsDisableSubMod);
|
||||
get => _canBeDisabled;
|
||||
set
|
||||
{
|
||||
_canBeDisabled = value;
|
||||
|
|
@ -92,8 +95,8 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer)
|
||||
=> new ImcModGroupEditDrawer(editDrawer, this);
|
||||
|
||||
public ImcManipulation GetManip(ushort mask)
|
||||
=> new(Identifier.ObjectType, Identifier.BodySlot, Identifier.PrimaryId, Identifier.SecondaryId.Id, Identifier.Variant.Id,
|
||||
public ImcManipulation GetManip(ushort mask, Variant variant)
|
||||
=> new(Identifier.ObjectType, Identifier.BodySlot, Identifier.PrimaryId, Identifier.SecondaryId.Id, variant.Id,
|
||||
Identifier.EquipSlot, DefaultEntry with { AttributeMask = mask });
|
||||
|
||||
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
|
||||
|
|
@ -102,12 +105,23 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
return;
|
||||
|
||||
var mask = GetCurrentMask(setting);
|
||||
var imc = GetManip(mask);
|
||||
manipulations.Add(imc);
|
||||
if (AllVariants)
|
||||
{
|
||||
var count = ImcChecker.GetVariantCount(Identifier);
|
||||
if (count == 0)
|
||||
manipulations.Add(GetManip(mask, Identifier.Variant));
|
||||
else
|
||||
for (var i = 0; i <= count; ++i)
|
||||
manipulations.Add(GetManip(mask, (Variant)i));
|
||||
}
|
||||
else
|
||||
{
|
||||
manipulations.Add(GetManip(mask, Identifier.Variant));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, object?> changedItems)
|
||||
=> Identifier.AddChangedItems(identifier, changedItems);
|
||||
=> Identifier.AddChangedItems(identifier, changedItems, AllVariants);
|
||||
|
||||
public Setting FixSetting(Setting setting)
|
||||
=> new(setting.Value & ((1ul << OptionData.Count) - 1));
|
||||
|
|
@ -120,6 +134,8 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
jObj.WriteTo(jWriter);
|
||||
jWriter.WritePropertyName(nameof(DefaultEntry));
|
||||
serializer.Serialize(jWriter, DefaultEntry);
|
||||
jWriter.WritePropertyName(nameof(AllVariants));
|
||||
jWriter.WriteValue(AllVariants);
|
||||
jWriter.WritePropertyName("Options");
|
||||
jWriter.WriteStartArray();
|
||||
foreach (var option in OptionData)
|
||||
|
|
@ -156,6 +172,7 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
Description = json[nameof(Description)]?.ToObject<string>() ?? string.Empty,
|
||||
Priority = json[nameof(Priority)]?.ToObject<ModPriority>() ?? ModPriority.Default,
|
||||
DefaultEntry = json[nameof(DefaultEntry)]?.ToObject<ImcEntry>() ?? new ImcEntry(),
|
||||
AllVariants = json[nameof(AllVariants)]?.ToObject<bool>() ?? false,
|
||||
};
|
||||
if (ret.Name.Length == 0)
|
||||
return null;
|
||||
|
|
@ -210,7 +227,7 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
if (idx >= 0)
|
||||
return setting.HasFlag(idx);
|
||||
|
||||
Penumbra.Log.Warning($"A IMC Group should be able to be disabled, but does not contain a disable option.");
|
||||
Penumbra.Log.Warning("A IMC Group should be able to be disabled, but does not contain a disable option.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public static class EquipmentSwap
|
|||
|
||||
var (imcFileFrom, variants, affectedItems) = GetVariants(manager, identifier, slotFrom, idFrom, idTo, variantFrom);
|
||||
var imcManip = new ImcManipulation(slotTo, variantTo.Id, idTo.Id, default);
|
||||
var imcFileTo = new ImcFile(manager, imcManip);
|
||||
var imcFileTo = new ImcFile(manager, imcManip.Identifier);
|
||||
var skipFemale = false;
|
||||
var skipMale = false;
|
||||
var mtrlVariantTo = manips(imcManip.Copy(imcFileTo.GetEntry(ImcFile.PartIndex(slotTo), variantTo.Id))).Imc.Entry.MaterialId;
|
||||
|
|
@ -121,7 +121,7 @@ public static class EquipmentSwap
|
|||
{
|
||||
(var imcFileFrom, var variants, affectedItems) = GetVariants(manager, identifier, slot, idFrom, idTo, variantFrom);
|
||||
var imcManip = new ImcManipulation(slot, variantTo.Id, idTo, default);
|
||||
var imcFileTo = new ImcFile(manager, imcManip);
|
||||
var imcFileTo = new ImcFile(manager, imcManip.Identifier);
|
||||
|
||||
var isAccessory = slot.IsAccessory();
|
||||
var estType = slot switch
|
||||
|
|
@ -250,7 +250,7 @@ public static class EquipmentSwap
|
|||
PrimaryId idFrom, PrimaryId idTo, Variant variantFrom)
|
||||
{
|
||||
var entry = new ImcManipulation(slotFrom, variantFrom.Id, idFrom, default);
|
||||
var imc = new ImcFile(manager, entry);
|
||||
var imc = new ImcFile(manager, entry.Identifier);
|
||||
EquipItem[] items;
|
||||
Variant[] variants;
|
||||
if (idFrom == idTo)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using OtterGui.Classes;
|
|||
using OtterGui.Filesystem;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods.Groups;
|
||||
using Penumbra.Mods.Settings;
|
||||
|
|
@ -14,7 +15,8 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
|
|||
: ModOptionEditor<ImcModGroup, ImcSubMod>(communicator, saveService, config), IService
|
||||
{
|
||||
/// <summary> Add a new, empty imc group with the given manipulation data. </summary>
|
||||
public ImcModGroup? AddModGroup(Mod mod, string newName, ImcIdentifier identifier, ImcEntry defaultEntry, SaveType saveType = SaveType.ImmediateSync)
|
||||
public ImcModGroup? AddModGroup(Mod mod, string newName, ImcIdentifier identifier, ImcEntry defaultEntry,
|
||||
SaveType saveType = SaveType.ImmediateSync)
|
||||
{
|
||||
if (!ModGroupEditor.VerifyFileName(mod, null, newName, true))
|
||||
return null;
|
||||
|
|
@ -78,6 +80,16 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
|
|||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, option.Mod, option.Group, option, null, -1);
|
||||
}
|
||||
|
||||
public void ChangeAllVariants(ImcModGroup group, bool allVariants, SaveType saveType = SaveType.Queue)
|
||||
{
|
||||
if (group.AllVariants == allVariants)
|
||||
return;
|
||||
|
||||
group.AllVariants = allVariants;
|
||||
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1);
|
||||
}
|
||||
|
||||
public void ChangeCanBeDisabled(ImcModGroup group, bool canBeDisabled, SaveType saveType = SaveType.Queue)
|
||||
{
|
||||
if (group.CanBeDisabled == canBeDisabled)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,13 @@ public readonly struct ImcModGroupEditDrawer(ModGroupEditDrawer editor, ImcModGr
|
|||
var entry = group.DefaultEntry;
|
||||
var changes = false;
|
||||
|
||||
ImUtf8.TextFramed(identifier.ToString(), 0, editor.AvailableWidth, borderColor: ImGui.GetColorU32(ImGuiCol.Border));
|
||||
var width = editor.AvailableWidth.X - ImUtf8.ItemInnerSpacing.X - ImUtf8.CalcTextSize("All Variants"u8).X;
|
||||
ImUtf8.TextFramed(identifier.ToString(), 0, new Vector2(width, 0), borderColor: ImGui.GetColorU32(ImGuiCol.Border));
|
||||
ImUtf8.SameLineInner();
|
||||
var allVariants = group.AllVariants;
|
||||
if (ImUtf8.Checkbox("All Variants"u8, ref allVariants))
|
||||
editor.ModManager.OptionEditor.ImcEditor.ChangeAllVariants(group, allVariants);
|
||||
ImUtf8.HoverTooltip("Make this group overwrite all corresponding variants for this identifier, not just the one specified."u8);
|
||||
|
||||
using (ImUtf8.Group())
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue