mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 13:14:17 +01:00
Improve GamePaths and parsing, add support for identifying skeletons and phybs.
This commit is contained in:
parent
8860d1e39a
commit
c6de7ddebd
17 changed files with 65 additions and 83 deletions
|
|
@ -1 +1 @@
|
||||||
Subproject commit f42c7fc9de98e9fc72680dee7805251fd938af26
|
Subproject commit bc339208d1d453582eb146533c572823146a4592
|
||||||
|
|
@ -63,7 +63,7 @@ public sealed class ModelManager(IFramework framework, MetaFileManager metaFileM
|
||||||
if (info.FileType is not FileType.Model)
|
if (info.FileType is not FileType.Model)
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
var baseSkeleton = GamePaths.Skeleton.Sklb.Path(info.GenderRace, "base", 1);
|
var baseSkeleton = GamePaths.Sklb.Customization(info.GenderRace, "base", 1);
|
||||||
|
|
||||||
return info.ObjectType switch
|
return info.ObjectType switch
|
||||||
{
|
{
|
||||||
|
|
@ -79,9 +79,9 @@ public sealed class ModelManager(IFramework framework, MetaFileManager metaFileM
|
||||||
ObjectType.Character when info.BodySlot is BodySlot.Face or BodySlot.Ear
|
ObjectType.Character when info.BodySlot is BodySlot.Face or BodySlot.Ear
|
||||||
=> [baseSkeleton, ..ResolveEstSkeleton(EstType.Face, info, estManipulations)],
|
=> [baseSkeleton, ..ResolveEstSkeleton(EstType.Face, info, estManipulations)],
|
||||||
ObjectType.Character => throw new Exception($"Currently unsupported human model type \"{info.BodySlot}\"."),
|
ObjectType.Character => throw new Exception($"Currently unsupported human model type \"{info.BodySlot}\"."),
|
||||||
ObjectType.DemiHuman => [GamePaths.DemiHuman.Sklb.Path(info.PrimaryId)],
|
ObjectType.DemiHuman => [GamePaths.Sklb.DemiHuman(info.PrimaryId)],
|
||||||
ObjectType.Monster => [GamePaths.Monster.Sklb.Path(info.PrimaryId)],
|
ObjectType.Monster => [GamePaths.Sklb.Monster(info.PrimaryId)],
|
||||||
ObjectType.Weapon => [GamePaths.Weapon.Sklb.Path(info.PrimaryId)],
|
ObjectType.Weapon => [GamePaths.Sklb.Weapon(info.PrimaryId)],
|
||||||
_ => [],
|
_ => [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +105,7 @@ public sealed class ModelManager(IFramework framework, MetaFileManager metaFileM
|
||||||
if (targetId == EstEntry.Zero)
|
if (targetId == EstEntry.Zero)
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
return [GamePaths.Skeleton.Sklb.Path(info.GenderRace, type.ToName(), targetId.AsId)];
|
return [GamePaths.Sklb.Customization(info.GenderRace, type.ToName(), targetId.AsId)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Try to resolve the absolute path to a .mtrl from the potentially-partial path provided by a model. </summary>
|
/// <summary> Try to resolve the absolute path to a .mtrl from the potentially-partial path provided by a model. </summary>
|
||||||
|
|
@ -137,7 +137,7 @@ public sealed class ModelManager(IFramework framework, MetaFileManager metaFileM
|
||||||
|
|
||||||
var resolvedPath = info.ObjectType switch
|
var resolvedPath = info.ObjectType switch
|
||||||
{
|
{
|
||||||
ObjectType.Character => GamePaths.Character.Mtrl.Path(
|
ObjectType.Character => GamePaths.Mtrl.Customization(
|
||||||
info.GenderRace, info.BodySlot, info.PrimaryId, relativePath, out _, out _, info.Variant),
|
info.GenderRace, info.BodySlot, info.PrimaryId, relativePath, out _, out _, info.Variant),
|
||||||
_ => absolutePath,
|
_ => absolutePath,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,8 @@ internal partial record ResolveContext
|
||||||
private Utf8GamePath ResolveEquipmentModelPath()
|
private Utf8GamePath ResolveEquipmentModelPath()
|
||||||
{
|
{
|
||||||
var path = IsEquipmentSlot(SlotIndex)
|
var path = IsEquipmentSlot(SlotIndex)
|
||||||
? GamePaths.Equipment.Mdl.Path(Equipment.Set, ResolveModelRaceCode(), SlotIndex.ToEquipSlot())
|
? GamePaths.Mdl.Equipment(Equipment.Set, ResolveModelRaceCode(), SlotIndex.ToEquipSlot())
|
||||||
: GamePaths.Accessory.Mdl.Path(Equipment.Set, ResolveModelRaceCode(), SlotIndex.ToEquipSlot());
|
: GamePaths.Mdl.Accessory(Equipment.Set, ResolveModelRaceCode(), SlotIndex.ToEquipSlot());
|
||||||
return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,7 +122,7 @@ internal partial record ResolveContext
|
||||||
var setIdHigh = Equipment.Set.Id / 100;
|
var setIdHigh = Equipment.Set.Id / 100;
|
||||||
// All MCH (20??) weapons' materials C are one and the same
|
// All MCH (20??) weapons' materials C are one and the same
|
||||||
if (setIdHigh is 20 && mtrlFileName[14] == (byte)'c')
|
if (setIdHigh is 20 && mtrlFileName[14] == (byte)'c')
|
||||||
return Utf8GamePath.FromString(GamePaths.Weapon.Mtrl.Path(2001, 1, 1, "c"), out var path) ? path : Utf8GamePath.Empty;
|
return Utf8GamePath.FromString(GamePaths.Mtrl.Weapon(2001, 1, 1, "c"), out var path) ? path : Utf8GamePath.Empty;
|
||||||
|
|
||||||
// Some offhands share materials with the corresponding mainhand
|
// Some offhands share materials with the corresponding mainhand
|
||||||
if (ItemData.AdaptOffhandImc(Equipment.Set, out var mirroredSetId))
|
if (ItemData.AdaptOffhandImc(Equipment.Set, out var mirroredSetId))
|
||||||
|
|
@ -230,7 +230,7 @@ internal partial record ResolveContext
|
||||||
if (set == 0)
|
if (set == 0)
|
||||||
return Utf8GamePath.Empty;
|
return Utf8GamePath.Empty;
|
||||||
|
|
||||||
var path = GamePaths.Skeleton.Sklb.Path(raceCode, slot, set);
|
var path = GamePaths.Sklb.Customization(raceCode, slot, set);
|
||||||
return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -300,7 +300,7 @@ internal partial record ResolveContext
|
||||||
if (set.Id is 0)
|
if (set.Id is 0)
|
||||||
return Utf8GamePath.Empty;
|
return Utf8GamePath.Empty;
|
||||||
|
|
||||||
var path = GamePaths.Skeleton.Skp.Path(raceCode, slot, set);
|
var path = GamePaths.Skp.Customization(raceCode, slot, set);
|
||||||
return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -328,7 +328,7 @@ internal partial record ResolveContext
|
||||||
if (set.Id is 0)
|
if (set.Id is 0)
|
||||||
return Utf8GamePath.Empty;
|
return Utf8GamePath.Empty;
|
||||||
|
|
||||||
var path = GamePaths.Skeleton.Phyb.Path(raceCode, slot, set);
|
var path = GamePaths.Phyb.Customization(raceCode, slot, set);
|
||||||
return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -354,7 +354,7 @@ internal partial record ResolveContext
|
||||||
if (decal is 0)
|
if (decal is 0)
|
||||||
return Utf8GamePath.Empty;
|
return Utf8GamePath.Empty;
|
||||||
|
|
||||||
var path = GamePaths.Equipment.Decal.Path(decal);
|
var path = GamePaths.Tex.EquipDecal(decal);
|
||||||
return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -355,13 +355,10 @@ internal unsafe partial record ResolveContext(
|
||||||
if (sklbHandle is null)
|
if (sklbHandle is null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (!Utf8GamePath.FromString(GamePaths.Skeleton.Sklb.MaterialAnimationSkeletonPath, out var path))
|
if (Global.Nodes.TryGetValue((GamePaths.Sklb.MaterialAnimationSkeletonUtf8, (nint)sklbHandle), out var cached))
|
||||||
return null;
|
|
||||||
|
|
||||||
if (Global.Nodes.TryGetValue((path, (nint)sklbHandle), out var cached))
|
|
||||||
return cached;
|
return cached;
|
||||||
|
|
||||||
var node = CreateNode(ResourceType.Sklb, 0, (ResourceHandle*)sklbHandle, path);
|
var node = CreateNode(ResourceType.Sklb, 0, (ResourceHandle*)sklbHandle, GamePaths.Sklb.MaterialAnimationSkeletonUtf8);
|
||||||
node.ForceInternal = true;
|
node.ForceInternal = true;
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
|
|
@ -455,11 +452,12 @@ internal unsafe partial record ResolveContext(
|
||||||
|
|
||||||
internal ResourceNode.UiData GuessUiDataFromPath(Utf8GamePath gamePath)
|
internal ResourceNode.UiData GuessUiDataFromPath(Utf8GamePath gamePath)
|
||||||
{
|
{
|
||||||
|
const string customization = "Customization: ";
|
||||||
foreach (var obj in Global.Identifier.Identify(gamePath.ToString()))
|
foreach (var obj in Global.Identifier.Identify(gamePath.ToString()))
|
||||||
{
|
{
|
||||||
var name = obj.Key;
|
var name = obj.Key;
|
||||||
if (obj.Value is IdentifiedCustomization)
|
if (name.StartsWith(customization))
|
||||||
name = name[14..].Trim();
|
name = name.AsSpan(14).Trim().ToString();
|
||||||
if (name is not "Unknown")
|
if (name is not "Unknown")
|
||||||
return new ResourceNode.UiData(name, obj.Value.GetIcon().ToFlag());
|
return new ResourceNode.UiData(name, obj.Value.GetIcon().ToFlag());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -207,8 +207,8 @@ public class ResourceTree(
|
||||||
|
|
||||||
var decalId = (byte)(human->Customize[(int)CustomizeIndex.Facepaint] & 0x7F);
|
var decalId = (byte)(human->Customize[(int)CustomizeIndex.Facepaint] & 0x7F);
|
||||||
var decalPath = decalId is not 0
|
var decalPath = decalId is not 0
|
||||||
? GamePaths.Human.Decal.FaceDecalPath(decalId)
|
? GamePaths.Tex.FaceDecal(decalId)
|
||||||
: GamePaths.Tex.TransparentPath;
|
: GamePaths.Tex.Transparent;
|
||||||
if (genericContext.CreateNodeFromTex(human->Decal, decalPath) is { } decalNode)
|
if (genericContext.CreateNodeFromTex(human->Decal, decalPath) is { } decalNode)
|
||||||
{
|
{
|
||||||
if (globalContext.WithUiData)
|
if (globalContext.WithUiData)
|
||||||
|
|
@ -223,8 +223,8 @@ public class ResourceTree(
|
||||||
|
|
||||||
var hasLegacyDecal = (human->Customize[(int)CustomizeIndex.FaceFeatures] & 0x80) != 0;
|
var hasLegacyDecal = (human->Customize[(int)CustomizeIndex.FaceFeatures] & 0x80) != 0;
|
||||||
var legacyDecalPath = hasLegacyDecal
|
var legacyDecalPath = hasLegacyDecal
|
||||||
? GamePaths.Human.Decal.LegacyDecalPath
|
? GamePaths.Tex.LegacyDecal
|
||||||
: GamePaths.Tex.TransparentPath;
|
: GamePaths.Tex.Transparent;
|
||||||
if (genericContext.CreateNodeFromTex(human->LegacyBodyDecal, legacyDecalPath) is { } legacyDecalNode)
|
if (genericContext.CreateNodeFromTex(human->LegacyBodyDecal, legacyDecalPath) is { } legacyDecalNode)
|
||||||
{
|
{
|
||||||
legacyDecalNode.ForceProtected = !hasLegacyDecal;
|
legacyDecalNode.ForceProtected = !hasLegacyDecal;
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ public readonly record struct EqdpIdentifier(PrimaryId SetId, EquipSlot Slot, Ge
|
||||||
=> GenderRace.Split().Item1;
|
=> GenderRace.Split().Item1;
|
||||||
|
|
||||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||||
=> identifier.Identify(changedItems, GamePaths.Equipment.Mdl.Path(SetId, GenderRace, Slot));
|
=> identifier.Identify(changedItems, GamePaths.Mdl.Equipment(SetId, GenderRace, Slot));
|
||||||
|
|
||||||
public MetaIndex FileIndex()
|
public MetaIndex FileIndex()
|
||||||
=> CharacterUtilityData.EqdpIdx(GenderRace, Slot.IsAccessory());
|
=> CharacterUtilityData.EqdpIdx(GenderRace, Slot.IsAccessory());
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace Penumbra.Meta.Manipulations;
|
||||||
public readonly record struct EqpIdentifier(PrimaryId SetId, EquipSlot Slot) : IMetaIdentifier, IComparable<EqpIdentifier>
|
public readonly record struct EqpIdentifier(PrimaryId SetId, EquipSlot Slot) : IMetaIdentifier, IComparable<EqpIdentifier>
|
||||||
{
|
{
|
||||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||||
=> identifier.Identify(changedItems, GamePaths.Equipment.Mdl.Path(SetId, GenderRace.MidlanderMale, Slot));
|
=> identifier.Identify(changedItems, GamePaths.Mdl.Equipment(SetId, GenderRace.MidlanderMale, Slot));
|
||||||
|
|
||||||
public MetaIndex FileIndex()
|
public MetaIndex FileIndex()
|
||||||
=> MetaIndex.Eqp;
|
=> MetaIndex.Eqp;
|
||||||
|
|
|
||||||
|
|
@ -30,17 +30,17 @@ public readonly record struct EstIdentifier(PrimaryId SetId, EstType Slot, Gende
|
||||||
{
|
{
|
||||||
case EstType.Hair:
|
case EstType.Hair:
|
||||||
changedItems.TryAdd(
|
changedItems.TryAdd(
|
||||||
$"Customization: {GenderRace.Split().Item2.ToName()} {GenderRace.Split().Item1.ToName()} Hair (Hair) {SetId}", null);
|
$"Customization: {GenderRace.Split().Item2.ToName()} {GenderRace.Split().Item1.ToName()} Hair {SetId}", null);
|
||||||
break;
|
break;
|
||||||
case EstType.Face:
|
case EstType.Face:
|
||||||
changedItems.TryAdd(
|
changedItems.TryAdd(
|
||||||
$"Customization: {GenderRace.Split().Item2.ToName()} {GenderRace.Split().Item1.ToName()} Face (Face) {SetId}", null);
|
$"Customization: {GenderRace.Split().Item2.ToName()} {GenderRace.Split().Item1.ToName()} Face {SetId}", null);
|
||||||
break;
|
break;
|
||||||
case EstType.Body:
|
case EstType.Body:
|
||||||
identifier.Identify(changedItems, GamePaths.Equipment.Mdl.Path(SetId, GenderRace, EquipSlot.Body));
|
identifier.Identify(changedItems, GamePaths.Mdl.Equipment(SetId, GenderRace, EquipSlot.Body));
|
||||||
break;
|
break;
|
||||||
case EstType.Head:
|
case EstType.Head:
|
||||||
identifier.Identify(changedItems, GamePaths.Equipment.Mdl.Path(SetId, GenderRace, EquipSlot.Head));
|
identifier.Identify(changedItems, GamePaths.Mdl.Equipment(SetId, GenderRace, EquipSlot.Head));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,11 +74,11 @@ public readonly struct GlobalEqpManipulation : IMetaIdentifier
|
||||||
{
|
{
|
||||||
var path = Type switch
|
var path = Type switch
|
||||||
{
|
{
|
||||||
GlobalEqpType.DoNotHideEarrings => GamePaths.Accessory.Mdl.Path(Condition, GenderRace.MidlanderMale, EquipSlot.Ears),
|
GlobalEqpType.DoNotHideEarrings => GamePaths.Mdl.Accessory(Condition, GenderRace.MidlanderMale, EquipSlot.Ears),
|
||||||
GlobalEqpType.DoNotHideNecklace => GamePaths.Accessory.Mdl.Path(Condition, GenderRace.MidlanderMale, EquipSlot.Neck),
|
GlobalEqpType.DoNotHideNecklace => GamePaths.Mdl.Accessory(Condition, GenderRace.MidlanderMale, EquipSlot.Neck),
|
||||||
GlobalEqpType.DoNotHideBracelets => GamePaths.Accessory.Mdl.Path(Condition, GenderRace.MidlanderMale, EquipSlot.Wrists),
|
GlobalEqpType.DoNotHideBracelets => GamePaths.Mdl.Accessory(Condition, GenderRace.MidlanderMale, EquipSlot.Wrists),
|
||||||
GlobalEqpType.DoNotHideRingR => GamePaths.Accessory.Mdl.Path(Condition, GenderRace.MidlanderMale, EquipSlot.RFinger),
|
GlobalEqpType.DoNotHideRingR => GamePaths.Mdl.Accessory(Condition, GenderRace.MidlanderMale, EquipSlot.RFinger),
|
||||||
GlobalEqpType.DoNotHideRingL => GamePaths.Accessory.Mdl.Path(Condition, GenderRace.MidlanderMale, EquipSlot.LFinger),
|
GlobalEqpType.DoNotHideRingL => GamePaths.Mdl.Accessory(Condition, GenderRace.MidlanderMale, EquipSlot.LFinger),
|
||||||
GlobalEqpType.DoNotHideHrothgarHats => string.Empty,
|
GlobalEqpType.DoNotHideHrothgarHats => string.Empty,
|
||||||
GlobalEqpType.DoNotHideVieraHats => string.Empty,
|
GlobalEqpType.DoNotHideVieraHats => string.Empty,
|
||||||
_ => string.Empty,
|
_ => string.Empty,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace Penumbra.Meta.Manipulations;
|
||||||
public readonly record struct GmpIdentifier(PrimaryId SetId) : IMetaIdentifier, IComparable<GmpIdentifier>
|
public readonly record struct GmpIdentifier(PrimaryId SetId) : IMetaIdentifier, IComparable<GmpIdentifier>
|
||||||
{
|
{
|
||||||
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, IIdentifiedObjectData?> changedItems)
|
||||||
=> identifier.Identify(changedItems, GamePaths.Equipment.Mdl.Path(SetId, GenderRace.MidlanderMale, EquipSlot.Head));
|
=> identifier.Identify(changedItems, GamePaths.Mdl.Equipment(SetId, GenderRace.MidlanderMale, EquipSlot.Head));
|
||||||
|
|
||||||
public MetaIndex FileIndex()
|
public MetaIndex FileIndex()
|
||||||
=> MetaIndex.Gmp;
|
=> MetaIndex.Gmp;
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,14 @@ public readonly record struct ImcIdentifier(
|
||||||
{
|
{
|
||||||
var path = ObjectType switch
|
var path = ObjectType switch
|
||||||
{
|
{
|
||||||
ObjectType.Equipment when allVariants => GamePaths.Equipment.Mdl.Path(PrimaryId, GenderRace.MidlanderMale, EquipSlot),
|
ObjectType.Equipment when allVariants => GamePaths.Mdl.Equipment(PrimaryId, GenderRace.MidlanderMale, EquipSlot),
|
||||||
ObjectType.Equipment => GamePaths.Equipment.Mtrl.Path(PrimaryId, GenderRace.MidlanderMale, EquipSlot, Variant, "a"),
|
ObjectType.Equipment => GamePaths.Mtrl.Equipment(PrimaryId, GenderRace.MidlanderMale, EquipSlot, Variant, "a"),
|
||||||
ObjectType.Accessory when allVariants => GamePaths.Accessory.Mdl.Path(PrimaryId, GenderRace.MidlanderMale, EquipSlot),
|
ObjectType.Accessory when allVariants => GamePaths.Mdl.Accessory(PrimaryId, GenderRace.MidlanderMale, EquipSlot),
|
||||||
ObjectType.Accessory => GamePaths.Accessory.Mtrl.Path(PrimaryId, GenderRace.MidlanderMale, EquipSlot, Variant, "a"),
|
ObjectType.Accessory => GamePaths.Mtrl.Accessory(PrimaryId, GenderRace.MidlanderMale, EquipSlot, Variant, "a"),
|
||||||
ObjectType.Weapon => GamePaths.Weapon.Mtrl.Path(PrimaryId, SecondaryId.Id, Variant, "a"),
|
ObjectType.Weapon => GamePaths.Mtrl.Weapon(PrimaryId, SecondaryId.Id, Variant, "a"),
|
||||||
ObjectType.DemiHuman => GamePaths.DemiHuman.Mtrl.Path(PrimaryId, SecondaryId.Id, EquipSlot, Variant,
|
ObjectType.DemiHuman => GamePaths.Mtrl.DemiHuman(PrimaryId, SecondaryId.Id, EquipSlot, Variant,
|
||||||
"a"),
|
"a"),
|
||||||
ObjectType.Monster => GamePaths.Monster.Mtrl.Path(PrimaryId, SecondaryId.Id, Variant, "a"),
|
ObjectType.Monster => GamePaths.Mtrl.Monster(PrimaryId, SecondaryId.Id, Variant, "a"),
|
||||||
_ => string.Empty,
|
_ => string.Empty,
|
||||||
};
|
};
|
||||||
if (path.Length == 0)
|
if (path.Length == 0)
|
||||||
|
|
@ -51,15 +51,7 @@ public readonly record struct ImcIdentifier(
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GamePathString()
|
public string GamePathString()
|
||||||
=> ObjectType switch
|
=> GamePaths.Imc.Path(ObjectType, PrimaryId, SecondaryId);
|
||||||
{
|
|
||||||
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()
|
public Utf8GamePath GamePath()
|
||||||
=> Utf8GamePath.FromString(GamePathString(), out var p) ? p : Utf8GamePath.Empty;
|
=> Utf8GamePath.FromString(GamePathString(), out var p) ? p : Utf8GamePath.Empty;
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ public static class CustomizationSwap
|
||||||
if (idFrom.Id > byte.MaxValue)
|
if (idFrom.Id > byte.MaxValue)
|
||||||
throw new Exception($"The Customization ID {idFrom} is too large for {slot}.");
|
throw new Exception($"The Customization ID {idFrom} is too large for {slot}.");
|
||||||
|
|
||||||
var mdlPathFrom = GamePaths.Character.Mdl.Path(race, slot, idFrom, slot.ToCustomizationType());
|
var mdlPathFrom = GamePaths.Mdl.Customization(race, slot, idFrom, slot.ToCustomizationType());
|
||||||
var mdlPathTo = GamePaths.Character.Mdl.Path(race, slot, idTo, slot.ToCustomizationType());
|
var mdlPathTo = GamePaths.Mdl.Customization(race, slot, idTo, slot.ToCustomizationType());
|
||||||
|
|
||||||
var mdl = FileSwap.CreateSwap(manager, ResourceType.Mdl, redirections, mdlPathFrom, mdlPathTo);
|
var mdl = FileSwap.CreateSwap(manager, ResourceType.Mdl, redirections, mdlPathFrom, mdlPathTo);
|
||||||
var range = slot == BodySlot.Tail
|
var range = slot == BodySlot.Tail
|
||||||
|
|
@ -47,8 +47,8 @@ public static class CustomizationSwap
|
||||||
ref string fileName, ref bool dataWasChanged)
|
ref string fileName, ref bool dataWasChanged)
|
||||||
{
|
{
|
||||||
variant = slot is BodySlot.Face or BodySlot.Ear ? Variant.None.Id : variant;
|
variant = slot is BodySlot.Face or BodySlot.Ear ? Variant.None.Id : variant;
|
||||||
var mtrlFromPath = GamePaths.Character.Mtrl.Path(race, slot, idFrom, fileName, out var gameRaceFrom, out var gameSetIdFrom, variant);
|
var mtrlFromPath = GamePaths.Mtrl.Customization(race, slot, idFrom, fileName, out var gameRaceFrom, out var gameSetIdFrom, variant);
|
||||||
var mtrlToPath = GamePaths.Character.Mtrl.Path(race, slot, idTo, fileName, out var gameRaceTo, out var gameSetIdTo, variant);
|
var mtrlToPath = GamePaths.Mtrl.Customization(race, slot, idTo, fileName, out var gameRaceTo, out var gameSetIdTo, variant);
|
||||||
|
|
||||||
var newFileName = fileName;
|
var newFileName = fileName;
|
||||||
newFileName = ItemSwap.ReplaceRace(newFileName, gameRaceTo, race, gameRaceTo != race);
|
newFileName = ItemSwap.ReplaceRace(newFileName, gameRaceTo, race, gameRaceTo != race);
|
||||||
|
|
@ -60,7 +60,7 @@ public static class CustomizationSwap
|
||||||
var actualMtrlFromPath = mtrlFromPath;
|
var actualMtrlFromPath = mtrlFromPath;
|
||||||
if (newFileName != fileName)
|
if (newFileName != fileName)
|
||||||
{
|
{
|
||||||
actualMtrlFromPath = GamePaths.Character.Mtrl.Path(race, slot, idFrom, newFileName, out _, out _, variant);
|
actualMtrlFromPath = GamePaths.Mtrl.Customization(race, slot, idFrom, newFileName, out _, out _, variant);
|
||||||
fileName = newFileName;
|
fileName = newFileName;
|
||||||
dataWasChanged = true;
|
dataWasChanged = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ public static class EquipmentSwap
|
||||||
foreach (var child in eqp.ChildSwaps.SelectMany(c => c.WithChildren()).OfType<MetaSwap<EqpIdentifier, EqpEntryInternal>>())
|
foreach (var child in eqp.ChildSwaps.SelectMany(c => c.WithChildren()).OfType<MetaSwap<EqpIdentifier, EqpEntryInternal>>())
|
||||||
{
|
{
|
||||||
affectedItems.UnionWith(identifier
|
affectedItems.UnionWith(identifier
|
||||||
.Identify(GamePaths.Equipment.Mdl.Path(idFrom, GenderRace.MidlanderMale, child.SwapFromIdentifier.Slot))
|
.Identify(GamePaths.Mdl.Equipment(idFrom, GenderRace.MidlanderMale, child.SwapFromIdentifier.Slot))
|
||||||
.Select(kvp => kvp.Value).OfType<IdentifiedItem>().Select(i => i.Item));
|
.Select(kvp => kvp.Value).OfType<IdentifiedItem>().Select(i => i.Item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -223,10 +223,8 @@ public static class EquipmentSwap
|
||||||
public static FileSwap CreateMdl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slotFrom, EquipSlot slotTo,
|
public static FileSwap CreateMdl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slotFrom, EquipSlot slotTo,
|
||||||
GenderRace gr, PrimaryId idFrom, PrimaryId idTo, byte mtrlTo)
|
GenderRace gr, PrimaryId idFrom, PrimaryId idTo, byte mtrlTo)
|
||||||
{
|
{
|
||||||
var mdlPathFrom = slotFrom.IsAccessory()
|
var mdlPathFrom = GamePaths.Mdl.Gear(idFrom, gr, slotFrom);
|
||||||
? GamePaths.Accessory.Mdl.Path(idFrom, gr, slotFrom)
|
var mdlPathTo = GamePaths.Mdl.Gear(idTo, gr, slotTo);
|
||||||
: GamePaths.Equipment.Mdl.Path(idFrom, gr, slotFrom);
|
|
||||||
var mdlPathTo = slotTo.IsAccessory() ? GamePaths.Accessory.Mdl.Path(idTo, gr, slotTo) : GamePaths.Equipment.Mdl.Path(idTo, gr, slotTo);
|
|
||||||
var mdl = FileSwap.CreateSwap(manager, ResourceType.Mdl, redirections, mdlPathFrom, mdlPathTo);
|
var mdl = FileSwap.CreateSwap(manager, ResourceType.Mdl, redirections, mdlPathFrom, mdlPathTo);
|
||||||
|
|
||||||
foreach (ref var fileName in mdl.AsMdl()!.Materials.AsSpan())
|
foreach (ref var fileName in mdl.AsMdl()!.Materials.AsSpan())
|
||||||
|
|
@ -264,9 +262,7 @@ public static class EquipmentSwap
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
items = identifier.Identify(slotFrom.IsEquipment()
|
items = identifier.Identify(GamePaths.Mdl.Gear(idFrom, GenderRace.MidlanderMale, slotFrom))
|
||||||
? GamePaths.Equipment.Mdl.Path(idFrom, GenderRace.MidlanderMale, slotFrom)
|
|
||||||
: GamePaths.Accessory.Mdl.Path(idFrom, GenderRace.MidlanderMale, slotFrom))
|
|
||||||
.Select(kvp => kvp.Value).OfType<IdentifiedItem>().Select(i => i.Item)
|
.Select(kvp => kvp.Value).OfType<IdentifiedItem>().Select(i => i.Item)
|
||||||
.ToHashSet();
|
.ToHashSet();
|
||||||
variants = Enumerable.Range(0, imc.Count + 1).Select(i => (Variant)i).ToArray();
|
variants = Enumerable.Range(0, imc.Count + 1).Select(i => (Variant)i).ToArray();
|
||||||
|
|
@ -324,7 +320,7 @@ public static class EquipmentSwap
|
||||||
if (decalId == 0)
|
if (decalId == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var decalPath = GamePaths.Equipment.Decal.Path(decalId);
|
var decalPath = GamePaths.Tex.EquipDecal(decalId);
|
||||||
return FileSwap.CreateSwap(manager, ResourceType.Tex, redirections, decalPath, decalPath);
|
return FileSwap.CreateSwap(manager, ResourceType.Tex, redirections, decalPath, decalPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -337,9 +333,9 @@ public static class EquipmentSwap
|
||||||
if (vfxId == 0)
|
if (vfxId == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var vfxPathFrom = GamePaths.Equipment.Avfx.Path(idFrom, vfxId);
|
var vfxPathFrom = GamePaths.Avfx.Path(slotFrom, idFrom, vfxId);
|
||||||
vfxPathFrom = ItemSwap.ReplaceType(vfxPathFrom, slotFrom, slotTo, idFrom);
|
vfxPathFrom = ItemSwap.ReplaceType(vfxPathFrom, slotFrom, slotTo, idFrom);
|
||||||
var vfxPathTo = GamePaths.Equipment.Avfx.Path(idTo, vfxId);
|
var vfxPathTo = GamePaths.Avfx.Path(slotTo, idTo, vfxId);
|
||||||
var avfx = FileSwap.CreateSwap(manager, ResourceType.Avfx, redirections, vfxPathFrom, vfxPathTo);
|
var avfx = FileSwap.CreateSwap(manager, ResourceType.Avfx, redirections, vfxPathFrom, vfxPathTo);
|
||||||
|
|
||||||
foreach (ref var filePath in avfx.AsAvfx()!.Textures.AsSpan())
|
foreach (ref var filePath in avfx.AsAvfx()!.Textures.AsSpan())
|
||||||
|
|
@ -402,14 +398,10 @@ public static class EquipmentSwap
|
||||||
if (!fileName.Contains($"{prefix}{idTo.Id:D4}"))
|
if (!fileName.Contains($"{prefix}{idTo.Id:D4}"))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var folderTo = slotTo.IsAccessory()
|
var folderTo = GamePaths.Mtrl.GearFolder(slotTo, idTo, variantTo);
|
||||||
? GamePaths.Accessory.Mtrl.FolderPath(idTo, variantTo)
|
|
||||||
: GamePaths.Equipment.Mtrl.FolderPath(idTo, variantTo);
|
|
||||||
var pathTo = $"{folderTo}{fileName}";
|
var pathTo = $"{folderTo}{fileName}";
|
||||||
|
|
||||||
var folderFrom = slotFrom.IsAccessory()
|
var folderFrom = GamePaths.Mtrl.GearFolder(slotFrom, idFrom, variantTo);
|
||||||
? GamePaths.Accessory.Mtrl.FolderPath(idFrom, variantTo)
|
|
||||||
: GamePaths.Equipment.Mtrl.FolderPath(idFrom, variantTo);
|
|
||||||
var newFileName = ItemSwap.ReplaceId(fileName, prefix, idTo, idFrom);
|
var newFileName = ItemSwap.ReplaceId(fileName, prefix, idTo, idFrom);
|
||||||
newFileName = ItemSwap.ReplaceSlot(newFileName, slotTo, slotFrom, slotTo != slotFrom);
|
newFileName = ItemSwap.ReplaceSlot(newFileName, slotTo, slotFrom, slotTo != slotFrom);
|
||||||
var pathFrom = $"{folderFrom}{newFileName}";
|
var pathFrom = $"{folderFrom}{newFileName}";
|
||||||
|
|
@ -457,7 +449,7 @@ public static class EquipmentSwap
|
||||||
public static FileSwap CreateShader(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, ref string shaderName,
|
public static FileSwap CreateShader(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, ref string shaderName,
|
||||||
ref bool dataWasChanged)
|
ref bool dataWasChanged)
|
||||||
{
|
{
|
||||||
var path = $"shader/sm5/shpk/{shaderName}";
|
var path = GamePaths.Shader(shaderName);
|
||||||
return FileSwap.CreateSwap(manager, ResourceType.Shpk, redirections, path, path);
|
return FileSwap.CreateSwap(manager, ResourceType.Shpk, redirections, path, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -132,14 +132,14 @@ public static class ItemSwap
|
||||||
public static FileSwap CreatePhyb(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EstType type,
|
public static FileSwap CreatePhyb(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EstType type,
|
||||||
GenderRace race, EstEntry estEntry)
|
GenderRace race, EstEntry estEntry)
|
||||||
{
|
{
|
||||||
var phybPath = GamePaths.Skeleton.Phyb.Path(race, type.ToName(), estEntry.AsId);
|
var phybPath = GamePaths.Phyb.Customization(race, type.ToName(), estEntry.AsId);
|
||||||
return FileSwap.CreateSwap(manager, ResourceType.Phyb, redirections, phybPath, phybPath);
|
return FileSwap.CreateSwap(manager, ResourceType.Phyb, redirections, phybPath, phybPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FileSwap CreateSklb(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EstType type,
|
public static FileSwap CreateSklb(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EstType type,
|
||||||
GenderRace race, EstEntry estEntry)
|
GenderRace race, EstEntry estEntry)
|
||||||
{
|
{
|
||||||
var sklbPath = GamePaths.Skeleton.Sklb.Path(race, type.ToName(), estEntry.AsId);
|
var sklbPath = GamePaths.Sklb.Customization(race, type.ToName(), estEntry.AsId);
|
||||||
return FileSwap.CreateSwap(manager, ResourceType.Sklb, redirections, sklbPath, sklbPath);
|
return FileSwap.CreateSwap(manager, ResourceType.Sklb, redirections, sklbPath, sklbPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ public partial class MtrlTab
|
||||||
|
|
||||||
private FullPath FindAssociatedShpk(out string defaultPath, out Utf8GamePath defaultGamePath)
|
private FullPath FindAssociatedShpk(out string defaultPath, out Utf8GamePath defaultGamePath)
|
||||||
{
|
{
|
||||||
defaultPath = GamePaths.Shader.ShpkPath(Mtrl.ShaderPackage.Name);
|
defaultPath = GamePaths.Shader(Mtrl.ShaderPackage.Name);
|
||||||
if (!Utf8GamePath.FromString(defaultPath, out defaultGamePath))
|
if (!Utf8GamePath.FromString(defaultPath, out defaultGamePath))
|
||||||
return FullPath.Empty;
|
return FullPath.Empty;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>, ISer
|
||||||
if (filePath.Length == 0 || !File.Exists(filePath))
|
if (filePath.Length == 0 || !File.Exists(filePath))
|
||||||
throw new FileNotFoundException();
|
throw new FileNotFoundException();
|
||||||
|
|
||||||
var gr = GamePaths.ParseRaceCode(filePath);
|
var gr = Parser.ParseRaceCode(filePath);
|
||||||
if (gr is GenderRace.Unknown)
|
if (gr is GenderRace.Unknown)
|
||||||
throw new Exception($"Could not identify race code from path {filePath}.");
|
throw new Exception($"Could not identify race code from path {filePath}.");
|
||||||
var text = File.ReadAllBytes(filePath);
|
var text = File.ReadAllBytes(filePath);
|
||||||
|
|
@ -277,7 +277,7 @@ public sealed class AtchMetaDrawer : MetaDrawer<AtchIdentifier, AtchEntry>, ISer
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
index = Math.Clamp(index, (ushort)0, (ushort)(currentAtchPoint!.Entries.Length - 1));
|
index = Math.Clamp(index, (ushort)0, (ushort)(currentAtchPoint.Entries.Length - 1));
|
||||||
identifier = identifier with { EntryIndex = index };
|
identifier = identifier with { EntryIndex = index };
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ public partial class ModEditWindow
|
||||||
{
|
{
|
||||||
_dragDropManager.CreateImGuiSource("atchDrag", f => f.Extensions.Contains(".atch"), f =>
|
_dragDropManager.CreateImGuiSource("atchDrag", f => f.Extensions.Contains(".atch"), f =>
|
||||||
{
|
{
|
||||||
var gr = GamePaths.ParseRaceCode(f.Files.FirstOrDefault() ?? string.Empty);
|
var gr = Parser.ParseRaceCode(f.Files.FirstOrDefault() ?? string.Empty);
|
||||||
if (gr is GenderRace.Unknown)
|
if (gr is GenderRace.Unknown)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue