mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-02-19 06:17:45 +01:00
Meta stuff is terrible.
This commit is contained in:
parent
0186f176d0
commit
1d82e882ed
35 changed files with 1265 additions and 1247 deletions
|
|
@ -5,6 +5,7 @@ using Penumbra.GameData.Data;
|
|||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Files;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Mods.ItemSwap;
|
||||
|
|
@ -12,7 +13,7 @@ namespace Penumbra.Mods.ItemSwap;
|
|||
public static class CustomizationSwap
|
||||
{
|
||||
/// The .mdl file for customizations is unique per racecode, slot and id, thus the .mdl redirection itself is independent of the mode.
|
||||
public static FileSwap CreateMdl( Func< Utf8GamePath, FullPath > redirections, BodySlot slot, GenderRace race, SetId idFrom, SetId idTo )
|
||||
public static FileSwap CreateMdl( MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, BodySlot slot, GenderRace race, SetId idFrom, SetId idTo )
|
||||
{
|
||||
if( idFrom.Value > byte.MaxValue )
|
||||
{
|
||||
|
|
@ -22,7 +23,7 @@ public static class CustomizationSwap
|
|||
var mdlPathFrom = GamePaths.Character.Mdl.Path( race, slot, idFrom, slot.ToCustomizationType() );
|
||||
var mdlPathTo = GamePaths.Character.Mdl.Path( race, slot, idTo, slot.ToCustomizationType() );
|
||||
|
||||
var mdl = FileSwap.CreateSwap( ResourceType.Mdl, redirections, mdlPathFrom, mdlPathTo );
|
||||
var mdl = FileSwap.CreateSwap( manager, ResourceType.Mdl, redirections, mdlPathFrom, mdlPathTo );
|
||||
var range = slot == BodySlot.Tail && race is GenderRace.HrothgarMale or GenderRace.HrothgarFemale or GenderRace.HrothgarMaleNpc or GenderRace.HrothgarMaleNpc ? 5 : 1;
|
||||
|
||||
foreach( ref var materialFileName in mdl.AsMdl()!.Materials.AsSpan() )
|
||||
|
|
@ -31,7 +32,7 @@ public static class CustomizationSwap
|
|||
foreach( var variant in Enumerable.Range( 1, range ) )
|
||||
{
|
||||
name = materialFileName;
|
||||
var mtrl = CreateMtrl( redirections, slot, race, idFrom, idTo, ( byte )variant, ref name, ref mdl.DataWasChanged );
|
||||
var mtrl = CreateMtrl( manager, redirections, slot, race, idFrom, idTo, ( byte )variant, ref name, ref mdl.DataWasChanged );
|
||||
mdl.ChildSwaps.Add( mtrl );
|
||||
}
|
||||
|
||||
|
|
@ -41,7 +42,7 @@ public static class CustomizationSwap
|
|||
return mdl;
|
||||
}
|
||||
|
||||
public static FileSwap CreateMtrl( Func< Utf8GamePath, FullPath > redirections, BodySlot slot, GenderRace race, SetId idFrom, SetId idTo, byte variant,
|
||||
public static FileSwap CreateMtrl( MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, BodySlot slot, GenderRace race, SetId idFrom, SetId idTo, byte variant,
|
||||
ref string fileName, ref bool dataWasChanged )
|
||||
{
|
||||
variant = slot is BodySlot.Face or BodySlot.Zear ? byte.MaxValue : variant;
|
||||
|
|
@ -62,20 +63,20 @@ public static class CustomizationSwap
|
|||
dataWasChanged = true;
|
||||
}
|
||||
|
||||
var mtrl = FileSwap.CreateSwap( ResourceType.Mtrl, redirections, actualMtrlFromPath, mtrlToPath, actualMtrlFromPath );
|
||||
var shpk = CreateShader( redirections, ref mtrl.AsMtrl()!.ShaderPackage.Name, ref mtrl.DataWasChanged );
|
||||
var mtrl = FileSwap.CreateSwap( manager, ResourceType.Mtrl, redirections, actualMtrlFromPath, mtrlToPath, actualMtrlFromPath );
|
||||
var shpk = CreateShader( manager, redirections, ref mtrl.AsMtrl()!.ShaderPackage.Name, ref mtrl.DataWasChanged );
|
||||
mtrl.ChildSwaps.Add( shpk );
|
||||
|
||||
foreach( ref var texture in mtrl.AsMtrl()!.Textures.AsSpan() )
|
||||
{
|
||||
var tex = CreateTex( redirections, slot, race, idFrom, ref texture, ref mtrl.DataWasChanged );
|
||||
var tex = CreateTex( manager, redirections, slot, race, idFrom, ref texture, ref mtrl.DataWasChanged );
|
||||
mtrl.ChildSwaps.Add( tex );
|
||||
}
|
||||
|
||||
return mtrl;
|
||||
}
|
||||
|
||||
public static FileSwap CreateTex( Func< Utf8GamePath, FullPath > redirections, BodySlot slot, GenderRace race, SetId idFrom, ref MtrlFile.Texture texture,
|
||||
public static FileSwap CreateTex( MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, BodySlot slot, GenderRace race, SetId idFrom, ref MtrlFile.Texture texture,
|
||||
ref bool dataWasChanged )
|
||||
{
|
||||
var path = texture.Path;
|
||||
|
|
@ -99,13 +100,13 @@ public static class CustomizationSwap
|
|||
dataWasChanged = true;
|
||||
}
|
||||
|
||||
return FileSwap.CreateSwap( ResourceType.Tex, redirections, newPath, path, path );
|
||||
return FileSwap.CreateSwap( manager, ResourceType.Tex, redirections, newPath, path, path );
|
||||
}
|
||||
|
||||
|
||||
public static FileSwap CreateShader( Func< Utf8GamePath, FullPath > redirections, ref string shaderName, ref bool dataWasChanged )
|
||||
public static FileSwap CreateShader( MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, ref string shaderName, ref bool dataWasChanged )
|
||||
{
|
||||
var path = $"shader/sm5/shpk/{shaderName}";
|
||||
return FileSwap.CreateSwap( ResourceType.Shpk, redirections, path, path );
|
||||
return FileSwap.CreateSwap( manager, ResourceType.Shpk, redirections, path, path );
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ using Penumbra.GameData.Enums;
|
|||
using Penumbra.GameData.Files;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.String.Classes;
|
||||
|
|
@ -18,41 +19,51 @@ namespace Penumbra.Mods.ItemSwap;
|
|||
|
||||
public static class EquipmentSwap
|
||||
{
|
||||
private static EquipSlot[] ConvertSlots( EquipSlot slot, bool rFinger, bool lFinger )
|
||||
private static EquipSlot[] ConvertSlots(EquipSlot slot, bool rFinger, bool lFinger)
|
||||
{
|
||||
if( slot != EquipSlot.RFinger )
|
||||
{
|
||||
return new[] { slot };
|
||||
}
|
||||
if (slot != EquipSlot.RFinger)
|
||||
return new[]
|
||||
{
|
||||
slot,
|
||||
};
|
||||
|
||||
return rFinger
|
||||
? lFinger
|
||||
? new[] { EquipSlot.RFinger, EquipSlot.LFinger }
|
||||
: new[] { EquipSlot.RFinger }
|
||||
? new[]
|
||||
{
|
||||
EquipSlot.RFinger,
|
||||
EquipSlot.LFinger,
|
||||
}
|
||||
: new[]
|
||||
{
|
||||
EquipSlot.RFinger,
|
||||
}
|
||||
: lFinger
|
||||
? new[] { EquipSlot.LFinger }
|
||||
: Array.Empty< EquipSlot >();
|
||||
? new[]
|
||||
{
|
||||
EquipSlot.LFinger,
|
||||
}
|
||||
: Array.Empty<EquipSlot>();
|
||||
}
|
||||
|
||||
public static Item[] CreateTypeSwap( IObjectIdentifier identifier, List< Swap > swaps, Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips,
|
||||
EquipSlot slotFrom, Item itemFrom, EquipSlot slotTo, Item itemTo )
|
||||
public static Item[] CreateTypeSwap(MetaFileManager manager, IObjectIdentifier identifier, List<Swap> swaps,
|
||||
Func<Utf8GamePath, FullPath> redirections, Func<MetaManipulation, MetaManipulation> manips,
|
||||
EquipSlot slotFrom, Item itemFrom, EquipSlot slotTo, Item itemTo)
|
||||
{
|
||||
LookupItem( itemFrom, out var actualSlotFrom, out var idFrom, out var variantFrom );
|
||||
LookupItem( itemTo, out var actualSlotTo, out var idTo, out var variantTo );
|
||||
if( actualSlotFrom != slotFrom.ToSlot() || actualSlotTo != slotTo.ToSlot() )
|
||||
{
|
||||
LookupItem(itemFrom, out var actualSlotFrom, out var idFrom, out var variantFrom);
|
||||
LookupItem(itemTo, out var actualSlotTo, out var idTo, out var variantTo);
|
||||
if (actualSlotFrom != slotFrom.ToSlot() || actualSlotTo != slotTo.ToSlot())
|
||||
throw new ItemSwap.InvalidItemTypeException();
|
||||
}
|
||||
|
||||
var ( imcFileFrom, variants, affectedItems ) = GetVariants( identifier, slotFrom, idFrom, idTo, variantFrom );
|
||||
var imcManip = new ImcManipulation( slotTo, variantTo, idTo.Value, default );
|
||||
var imcFileTo = new ImcFile( imcManip );
|
||||
var (imcFileFrom, variants, affectedItems) = GetVariants(manager, identifier, slotFrom, idFrom, idTo, variantFrom);
|
||||
var imcManip = new ImcManipulation(slotTo, variantTo, idTo.Value, default);
|
||||
var imcFileTo = new ImcFile(manager, imcManip);
|
||||
var skipFemale = false;
|
||||
var skipMale = false;
|
||||
var mtrlVariantTo = manips( imcManip.Copy( imcFileTo.GetEntry( ImcFile.PartIndex( slotTo ), variantTo ) ) ).Imc.Entry.MaterialId;
|
||||
foreach( var gr in Enum.GetValues< GenderRace >() )
|
||||
var mtrlVariantTo = manips(imcManip.Copy(imcFileTo.GetEntry(ImcFile.PartIndex(slotTo), variantTo))).Imc.Entry.MaterialId;
|
||||
foreach (var gr in Enum.GetValues<GenderRace>())
|
||||
{
|
||||
switch( gr.Split().Item1 )
|
||||
switch (gr.Split().Item1)
|
||||
{
|
||||
case Gender.Male when skipMale: continue;
|
||||
case Gender.Female when skipFemale: continue;
|
||||
|
|
@ -60,22 +71,18 @@ public static class EquipmentSwap
|
|||
case Gender.FemaleNpc when skipFemale: continue;
|
||||
}
|
||||
|
||||
if( CharacterUtilityData.EqdpIdx( gr, true ) < 0 )
|
||||
{
|
||||
if (CharacterUtilityData.EqdpIdx(gr, true) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var eqdp = CreateEqdp( redirections, manips, slotFrom, slotTo, gr, idFrom, idTo, mtrlVariantTo );
|
||||
if( eqdp != null )
|
||||
{
|
||||
swaps.Add( eqdp );
|
||||
}
|
||||
var eqdp = CreateEqdp(manager, redirections, manips, slotFrom, slotTo, gr, idFrom, idTo, mtrlVariantTo);
|
||||
if (eqdp != null)
|
||||
swaps.Add(eqdp);
|
||||
}
|
||||
catch( ItemSwap.MissingFileException e )
|
||||
catch (ItemSwap.MissingFileException e)
|
||||
{
|
||||
switch( gr )
|
||||
switch (gr)
|
||||
{
|
||||
case GenderRace.MidlanderMale when e.Type == ResourceType.Mdl:
|
||||
skipMale = true;
|
||||
|
|
@ -88,59 +95,54 @@ public static class EquipmentSwap
|
|||
}
|
||||
}
|
||||
|
||||
foreach( var variant in variants )
|
||||
foreach (var variant in variants)
|
||||
{
|
||||
var imc = CreateImc( redirections, manips, slotFrom, slotTo, idFrom, idTo, variant, variantTo, imcFileFrom, imcFileTo );
|
||||
swaps.Add( imc );
|
||||
var imc = CreateImc(manager, redirections, manips, slotFrom, slotTo, idFrom, idTo, variant, variantTo, imcFileFrom, imcFileTo);
|
||||
swaps.Add(imc);
|
||||
}
|
||||
|
||||
return affectedItems;
|
||||
}
|
||||
|
||||
public static Item[] CreateItemSwap( IObjectIdentifier identifier, List< Swap > swaps, Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips, Item itemFrom,
|
||||
Item itemTo, bool rFinger = true, bool lFinger = true )
|
||||
public static Item[] CreateItemSwap(MetaFileManager manager, IObjectIdentifier identifier, List<Swap> swaps,
|
||||
Func<Utf8GamePath, FullPath> redirections, Func<MetaManipulation, MetaManipulation> manips, Item itemFrom,
|
||||
Item itemTo, bool rFinger = true, bool lFinger = true)
|
||||
{
|
||||
// Check actual ids, variants and slots. We only support using the same slot.
|
||||
LookupItem( itemFrom, out var slotFrom, out var idFrom, out var variantFrom );
|
||||
LookupItem( itemTo, out var slotTo, out var idTo, out var variantTo );
|
||||
if( slotFrom != slotTo )
|
||||
{
|
||||
LookupItem(itemFrom, out var slotFrom, out var idFrom, out var variantFrom);
|
||||
LookupItem(itemTo, out var slotTo, out var idTo, out var variantTo);
|
||||
if (slotFrom != slotTo)
|
||||
throw new ItemSwap.InvalidItemTypeException();
|
||||
}
|
||||
|
||||
var eqp = CreateEqp( manips, slotFrom, idFrom, idTo );
|
||||
if( eqp != null )
|
||||
{
|
||||
swaps.Add( eqp );
|
||||
}
|
||||
var eqp = CreateEqp(manager, manips, slotFrom, idFrom, idTo);
|
||||
if (eqp != null)
|
||||
swaps.Add(eqp);
|
||||
|
||||
var gmp = CreateGmp( manips, slotFrom, idFrom, idTo );
|
||||
if( gmp != null )
|
||||
{
|
||||
swaps.Add( gmp );
|
||||
}
|
||||
var gmp = CreateGmp(manager, manips, slotFrom, idFrom, idTo);
|
||||
if (gmp != null)
|
||||
swaps.Add(gmp);
|
||||
|
||||
var affectedItems = Array.Empty< Item >();
|
||||
foreach( var slot in ConvertSlots( slotFrom, rFinger, lFinger ) )
|
||||
var affectedItems = Array.Empty<Item>();
|
||||
foreach (var slot in ConvertSlots(slotFrom, rFinger, lFinger))
|
||||
{
|
||||
( var imcFileFrom, var variants, affectedItems ) = GetVariants( identifier, slot, idFrom, idTo, variantFrom );
|
||||
var imcManip = new ImcManipulation( slot, variantTo, idTo.Value, default );
|
||||
var imcFileTo = new ImcFile( imcManip );
|
||||
(var imcFileFrom, var variants, affectedItems) = GetVariants(manager, identifier, slot, idFrom, idTo, variantFrom);
|
||||
var imcManip = new ImcManipulation(slot, variantTo, idTo.Value, default);
|
||||
var imcFileTo = new ImcFile(manager, imcManip);
|
||||
|
||||
var isAccessory = slot.IsAccessory();
|
||||
var estType = slot switch
|
||||
{
|
||||
EquipSlot.Head => EstManipulation.EstType.Head,
|
||||
EquipSlot.Body => EstManipulation.EstType.Body,
|
||||
_ => ( EstManipulation.EstType )0,
|
||||
_ => (EstManipulation.EstType)0,
|
||||
};
|
||||
|
||||
var skipFemale = false;
|
||||
var skipMale = false;
|
||||
var mtrlVariantTo = manips( imcManip.Copy( imcFileTo.GetEntry( ImcFile.PartIndex( slot ), variantTo ) ) ).Imc.Entry.MaterialId;
|
||||
foreach( var gr in Enum.GetValues< GenderRace >() )
|
||||
var mtrlVariantTo = manips(imcManip.Copy(imcFileTo.GetEntry(ImcFile.PartIndex(slot), variantTo))).Imc.Entry.MaterialId;
|
||||
foreach (var gr in Enum.GetValues<GenderRace>())
|
||||
{
|
||||
switch( gr.Split().Item1 )
|
||||
switch (gr.Split().Item1)
|
||||
{
|
||||
case Gender.Male when skipMale: continue;
|
||||
case Gender.Female when skipFemale: continue;
|
||||
|
|
@ -148,30 +150,24 @@ public static class EquipmentSwap
|
|||
case Gender.FemaleNpc when skipFemale: continue;
|
||||
}
|
||||
|
||||
if( CharacterUtilityData.EqdpIdx( gr, isAccessory ) < 0 )
|
||||
{
|
||||
if (CharacterUtilityData.EqdpIdx(gr, isAccessory) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var eqdp = CreateEqdp( redirections, manips, slot, gr, idFrom, idTo, mtrlVariantTo );
|
||||
if( eqdp != null )
|
||||
{
|
||||
swaps.Add( eqdp );
|
||||
}
|
||||
var eqdp = CreateEqdp(manager, redirections, manips, slot, gr, idFrom, idTo, mtrlVariantTo);
|
||||
if (eqdp != null)
|
||||
swaps.Add(eqdp);
|
||||
|
||||
var ownMdl = eqdp?.SwapApplied.Eqdp.Entry.ToBits( slot ).Item2 ?? false;
|
||||
var est = ItemSwap.CreateEst( redirections, manips, estType, gr, idFrom, idTo, ownMdl );
|
||||
if( est != null )
|
||||
{
|
||||
swaps.Add( est );
|
||||
}
|
||||
var ownMdl = eqdp?.SwapApplied.Eqdp.Entry.ToBits(slot).Item2 ?? false;
|
||||
var est = ItemSwap.CreateEst(manager, redirections, manips, estType, gr, idFrom, idTo, ownMdl);
|
||||
if (est != null)
|
||||
swaps.Add(est);
|
||||
}
|
||||
catch( ItemSwap.MissingFileException e )
|
||||
catch (ItemSwap.MissingFileException e)
|
||||
{
|
||||
switch( gr )
|
||||
switch (gr)
|
||||
{
|
||||
case GenderRace.MidlanderMale when e.Type == ResourceType.Mdl:
|
||||
skipMale = true;
|
||||
|
|
@ -184,33 +180,38 @@ public static class EquipmentSwap
|
|||
}
|
||||
}
|
||||
|
||||
foreach( var variant in variants )
|
||||
foreach (var variant in variants)
|
||||
{
|
||||
var imc = CreateImc( redirections, manips, slot, idFrom, idTo, variant, variantTo, imcFileFrom, imcFileTo );
|
||||
swaps.Add( imc );
|
||||
var imc = CreateImc(manager, redirections, manips, slot, idFrom, idTo, variant, variantTo, imcFileFrom, imcFileTo);
|
||||
swaps.Add(imc);
|
||||
}
|
||||
}
|
||||
|
||||
return affectedItems;
|
||||
}
|
||||
|
||||
public static MetaSwap? CreateEqdp( Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips, EquipSlot slot, GenderRace gr, SetId idFrom,
|
||||
SetId idTo, byte mtrlTo )
|
||||
=> CreateEqdp( redirections, manips, slot, slot, gr, idFrom, idTo, mtrlTo );
|
||||
public static MetaSwap? CreateEqdp( Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips, EquipSlot slotFrom, EquipSlot slotTo, GenderRace gr, SetId idFrom,
|
||||
SetId idTo, byte mtrlTo )
|
||||
public static MetaSwap? CreateEqdp(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
||||
Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, GenderRace gr, SetId idFrom,
|
||||
SetId idTo, byte mtrlTo)
|
||||
=> CreateEqdp(manager, redirections, manips, slot, slot, gr, idFrom, idTo, mtrlTo);
|
||||
|
||||
public static MetaSwap? CreateEqdp(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections,
|
||||
Func<MetaManipulation, MetaManipulation> manips, EquipSlot slotFrom, EquipSlot slotTo, GenderRace gr, SetId idFrom,
|
||||
SetId idTo, byte mtrlTo)
|
||||
{
|
||||
var (gender, race) = gr.Split();
|
||||
var eqdpFrom = new EqdpManipulation( ExpandedEqdpFile.GetDefault( gr, slotFrom.IsAccessory(), idFrom.Value ), slotFrom, gender, race, idFrom.Value );
|
||||
var eqdpTo = new EqdpManipulation( ExpandedEqdpFile.GetDefault( gr, slotTo.IsAccessory(), idTo.Value ), slotTo, gender, race, idTo.Value );
|
||||
var meta = new MetaSwap( manips, eqdpFrom, eqdpTo );
|
||||
var (ownMtrl, ownMdl) = meta.SwapApplied.Eqdp.Entry.ToBits( slotFrom );
|
||||
if( ownMdl )
|
||||
var eqdpFrom = new EqdpManipulation(ExpandedEqdpFile.GetDefault(manager, gr, slotFrom.IsAccessory(), idFrom.Value), slotFrom, gender,
|
||||
race, idFrom.Value);
|
||||
var eqdpTo = new EqdpManipulation(ExpandedEqdpFile.GetDefault(manager, gr, slotTo.IsAccessory(), idTo.Value), slotTo, gender, race,
|
||||
idTo.Value);
|
||||
var meta = new MetaSwap(manips, eqdpFrom, eqdpTo);
|
||||
var (ownMtrl, ownMdl) = meta.SwapApplied.Eqdp.Entry.ToBits(slotFrom);
|
||||
if (ownMdl)
|
||||
{
|
||||
var mdl = CreateMdl( redirections, slotFrom, slotTo, gr, idFrom, idTo, mtrlTo );
|
||||
meta.ChildSwaps.Add( mdl );
|
||||
var mdl = CreateMdl(manager, redirections, slotFrom, slotTo, gr, idFrom, idTo, mtrlTo);
|
||||
meta.ChildSwaps.Add(mdl);
|
||||
}
|
||||
else if( !ownMtrl && meta.SwapAppliedIsDefault )
|
||||
else if (!ownMtrl && meta.SwapAppliedIsDefault)
|
||||
{
|
||||
meta = null;
|
||||
}
|
||||
|
|
@ -218,97 +219,98 @@ public static class EquipmentSwap
|
|||
return meta;
|
||||
}
|
||||
|
||||
public static FileSwap CreateMdl( Func< Utf8GamePath, FullPath > redirections, EquipSlot slot, GenderRace gr, SetId idFrom, SetId idTo, byte mtrlTo )
|
||||
=> CreateMdl( redirections, slot, slot, gr, idFrom, idTo, mtrlTo );
|
||||
public static FileSwap CreateMdl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slot, GenderRace gr,
|
||||
SetId idFrom, SetId idTo, byte mtrlTo)
|
||||
=> CreateMdl(manager, redirections, slot, slot, gr, idFrom, idTo, mtrlTo);
|
||||
|
||||
public static FileSwap CreateMdl( Func< Utf8GamePath, FullPath > redirections, EquipSlot slotFrom, EquipSlot slotTo, GenderRace gr, SetId idFrom, SetId idTo, byte mtrlTo )
|
||||
public static FileSwap CreateMdl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slotFrom, EquipSlot slotTo,
|
||||
GenderRace gr, SetId idFrom, SetId idTo, byte mtrlTo)
|
||||
{
|
||||
var mdlPathFrom = slotFrom.IsAccessory() ? GamePaths.Accessory.Mdl.Path( idFrom, gr, slotFrom ) : 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( ResourceType.Mdl, redirections, mdlPathFrom, mdlPathTo );
|
||||
var mdlPathFrom = slotFrom.IsAccessory()
|
||||
? GamePaths.Accessory.Mdl.Path(idFrom, gr, slotFrom)
|
||||
: 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);
|
||||
|
||||
foreach( ref var fileName in mdl.AsMdl()!.Materials.AsSpan() )
|
||||
foreach (ref var fileName in mdl.AsMdl()!.Materials.AsSpan())
|
||||
{
|
||||
var mtrl = CreateMtrl( redirections, slotFrom, slotTo, idFrom, idTo, mtrlTo, ref fileName, ref mdl.DataWasChanged );
|
||||
if( mtrl != null )
|
||||
{
|
||||
mdl.ChildSwaps.Add( mtrl );
|
||||
}
|
||||
var mtrl = CreateMtrl(manager, redirections, slotFrom, slotTo, idFrom, idTo, mtrlTo, ref fileName, ref mdl.DataWasChanged);
|
||||
if (mtrl != null)
|
||||
mdl.ChildSwaps.Add(mtrl);
|
||||
}
|
||||
|
||||
return mdl;
|
||||
}
|
||||
|
||||
private static void LookupItem( Item i, out EquipSlot slot, out SetId modelId, out byte variant )
|
||||
private static void LookupItem(Item i, out EquipSlot slot, out SetId modelId, out byte variant)
|
||||
{
|
||||
slot = ( ( EquipSlot )i.EquipSlotCategory.Row ).ToSlot();
|
||||
if( !slot.IsEquipmentPiece() )
|
||||
{
|
||||
slot = ((EquipSlot)i.EquipSlotCategory.Row).ToSlot();
|
||||
if (!slot.IsEquipmentPiece())
|
||||
throw new ItemSwap.InvalidItemTypeException();
|
||||
}
|
||||
|
||||
modelId = ( ( Quad )i.ModelMain ).A;
|
||||
variant = ( byte )( ( Quad )i.ModelMain ).B;
|
||||
modelId = ((Quad)i.ModelMain).A;
|
||||
variant = (byte)((Quad)i.ModelMain).B;
|
||||
}
|
||||
|
||||
private static (ImcFile, byte[], Item[]) GetVariants( IObjectIdentifier identifier, EquipSlot slotFrom, SetId idFrom, SetId idTo, byte variantFrom )
|
||||
private static (ImcFile, byte[], Item[]) GetVariants(MetaFileManager manager, IObjectIdentifier identifier, EquipSlot slotFrom,
|
||||
SetId idFrom, SetId idTo, byte variantFrom)
|
||||
{
|
||||
var entry = new ImcManipulation( slotFrom, variantFrom, idFrom.Value, default );
|
||||
var imc = new ImcFile( entry );
|
||||
var entry = new ImcManipulation(slotFrom, variantFrom, idFrom.Value, default);
|
||||
var imc = new ImcFile(manager, entry);
|
||||
Item[] items;
|
||||
byte[] variants;
|
||||
if( idFrom.Value == idTo.Value )
|
||||
if (idFrom.Value == idTo.Value)
|
||||
{
|
||||
items = identifier.Identify( idFrom, variantFrom, slotFrom ).ToArray();
|
||||
variants = new[] { variantFrom };
|
||||
items = identifier.Identify(idFrom, variantFrom, slotFrom).ToArray();
|
||||
variants = new[]
|
||||
{
|
||||
variantFrom,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
items = identifier.Identify( slotFrom.IsEquipment()
|
||||
? GamePaths.Equipment.Mdl.Path( idFrom, GenderRace.MidlanderMale, slotFrom )
|
||||
: GamePaths.Accessory.Mdl.Path( idFrom, GenderRace.MidlanderMale, slotFrom ) ).Select( kvp => kvp.Value ).OfType< Item >().ToArray();
|
||||
variants = Enumerable.Range( 0, imc.Count + 1 ).Select( i => ( byte )i ).ToArray();
|
||||
items = identifier.Identify(slotFrom.IsEquipment()
|
||||
? GamePaths.Equipment.Mdl.Path(idFrom, GenderRace.MidlanderMale, slotFrom)
|
||||
: GamePaths.Accessory.Mdl.Path(idFrom, GenderRace.MidlanderMale, slotFrom)).Select(kvp => kvp.Value).OfType<Item>().ToArray();
|
||||
variants = Enumerable.Range(0, imc.Count + 1).Select(i => (byte)i).ToArray();
|
||||
}
|
||||
|
||||
return ( imc, variants, items );
|
||||
return (imc, variants, items);
|
||||
}
|
||||
|
||||
public static MetaSwap? CreateGmp( Func< MetaManipulation, MetaManipulation > manips, EquipSlot slot, SetId idFrom, SetId idTo )
|
||||
public static MetaSwap? CreateGmp(MetaFileManager manager, Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, SetId idFrom,
|
||||
SetId idTo)
|
||||
{
|
||||
if( slot is not EquipSlot.Head )
|
||||
{
|
||||
if (slot is not EquipSlot.Head)
|
||||
return null;
|
||||
}
|
||||
|
||||
var manipFrom = new GmpManipulation( ExpandedGmpFile.GetDefault( idFrom.Value ), idFrom.Value );
|
||||
var manipTo = new GmpManipulation( ExpandedGmpFile.GetDefault( idTo.Value ), idTo.Value );
|
||||
return new MetaSwap( manips, manipFrom, manipTo );
|
||||
var manipFrom = new GmpManipulation(ExpandedGmpFile.GetDefault(manager, idFrom.Value), idFrom.Value);
|
||||
var manipTo = new GmpManipulation(ExpandedGmpFile.GetDefault(manager, idTo.Value), idTo.Value);
|
||||
return new MetaSwap(manips, manipFrom, manipTo);
|
||||
}
|
||||
|
||||
public static MetaSwap CreateImc( Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips, EquipSlot slot, SetId idFrom, SetId idTo,
|
||||
byte variantFrom, byte variantTo, ImcFile imcFileFrom, ImcFile imcFileTo )
|
||||
=> CreateImc( redirections, manips, slot, slot, idFrom, idTo, variantFrom, variantTo, imcFileFrom, imcFileTo );
|
||||
public static MetaSwap CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot,
|
||||
SetId idFrom, SetId idTo,
|
||||
byte variantFrom, byte variantTo, ImcFile imcFileFrom, ImcFile imcFileTo)
|
||||
=> CreateImc(manager, redirections, manips, slot, slot, idFrom, idTo, variantFrom, variantTo, imcFileFrom, imcFileTo);
|
||||
|
||||
public static MetaSwap CreateImc( Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips, EquipSlot slotFrom, EquipSlot slotTo, SetId idFrom, SetId idTo,
|
||||
byte variantFrom, byte variantTo, ImcFile imcFileFrom, ImcFile imcFileTo )
|
||||
public static MetaSwap CreateImc(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, Func<MetaManipulation, MetaManipulation> manips,
|
||||
EquipSlot slotFrom, EquipSlot slotTo, SetId idFrom, SetId idTo,
|
||||
byte variantFrom, byte variantTo, ImcFile imcFileFrom, ImcFile imcFileTo)
|
||||
{
|
||||
var entryFrom = imcFileFrom.GetEntry( ImcFile.PartIndex( slotFrom ), variantFrom );
|
||||
var entryTo = imcFileTo.GetEntry( ImcFile.PartIndex( slotTo ), variantTo );
|
||||
var manipulationFrom = new ImcManipulation( slotFrom, variantFrom, idFrom.Value, entryFrom );
|
||||
var manipulationTo = new ImcManipulation( slotTo, variantTo, idTo.Value, entryTo );
|
||||
var imc = new MetaSwap( manips, manipulationFrom, manipulationTo );
|
||||
var entryFrom = imcFileFrom.GetEntry(ImcFile.PartIndex(slotFrom), variantFrom);
|
||||
var entryTo = imcFileTo.GetEntry(ImcFile.PartIndex(slotTo), variantTo);
|
||||
var manipulationFrom = new ImcManipulation(slotFrom, variantFrom, idFrom.Value, entryFrom);
|
||||
var manipulationTo = new ImcManipulation(slotTo, variantTo, idTo.Value, entryTo);
|
||||
var imc = new MetaSwap(manips, manipulationFrom, manipulationTo);
|
||||
|
||||
var decal = CreateDecal( redirections, imc.SwapToModded.Imc.Entry.DecalId );
|
||||
if( decal != null )
|
||||
{
|
||||
imc.ChildSwaps.Add( decal );
|
||||
}
|
||||
var decal = CreateDecal(manager, redirections, imc.SwapToModded.Imc.Entry.DecalId);
|
||||
if (decal != null)
|
||||
imc.ChildSwaps.Add(decal);
|
||||
|
||||
var avfx = CreateAvfx( redirections, idFrom, idTo, imc.SwapToModded.Imc.Entry.VfxId );
|
||||
if( avfx != null )
|
||||
{
|
||||
imc.ChildSwaps.Add( avfx );
|
||||
}
|
||||
var avfx = CreateAvfx(manager, redirections, idFrom, idTo, imc.SwapToModded.Imc.Entry.VfxId);
|
||||
if (avfx != null)
|
||||
imc.ChildSwaps.Add(avfx);
|
||||
|
||||
// IMC also controls sound, Example: Dodore Doublet, but unknown what it does?
|
||||
// IMC also controls some material animation, Example: The Howling Spirit and The Wailing Spirit, but unknown what it does.
|
||||
|
|
@ -316,134 +318,135 @@ public static class EquipmentSwap
|
|||
}
|
||||
|
||||
// Example: Crimson Standard Bracelet
|
||||
public static FileSwap? CreateDecal( Func< Utf8GamePath, FullPath > redirections, byte decalId )
|
||||
public static FileSwap? CreateDecal(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, byte decalId)
|
||||
{
|
||||
if( decalId == 0 )
|
||||
{
|
||||
if (decalId == 0)
|
||||
return null;
|
||||
}
|
||||
|
||||
var decalPath = GamePaths.Equipment.Decal.Path( decalId );
|
||||
return FileSwap.CreateSwap( ResourceType.Tex, redirections, decalPath, decalPath );
|
||||
var decalPath = GamePaths.Equipment.Decal.Path(decalId);
|
||||
return FileSwap.CreateSwap(manager, ResourceType.Tex, redirections, decalPath, decalPath);
|
||||
}
|
||||
|
||||
|
||||
// Example: Abyssos Helm / Body
|
||||
public static FileSwap? CreateAvfx( Func< Utf8GamePath, FullPath > redirections, SetId idFrom, SetId idTo, byte vfxId )
|
||||
public static FileSwap? CreateAvfx(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, SetId idFrom, SetId idTo, byte vfxId)
|
||||
{
|
||||
if( vfxId == 0 )
|
||||
{
|
||||
if (vfxId == 0)
|
||||
return null;
|
||||
}
|
||||
|
||||
var vfxPathFrom = GamePaths.Equipment.Avfx.Path( idFrom.Value, vfxId );
|
||||
var vfxPathTo = GamePaths.Equipment.Avfx.Path( idTo.Value, vfxId );
|
||||
var avfx = FileSwap.CreateSwap( ResourceType.Avfx, redirections, vfxPathFrom, vfxPathTo );
|
||||
var vfxPathFrom = GamePaths.Equipment.Avfx.Path(idFrom.Value, vfxId);
|
||||
var vfxPathTo = GamePaths.Equipment.Avfx.Path(idTo.Value, vfxId);
|
||||
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())
|
||||
{
|
||||
var atex = CreateAtex( redirections, ref filePath, ref avfx.DataWasChanged );
|
||||
avfx.ChildSwaps.Add( atex );
|
||||
var atex = CreateAtex(manager, redirections, ref filePath, ref avfx.DataWasChanged);
|
||||
avfx.ChildSwaps.Add(atex);
|
||||
}
|
||||
|
||||
return avfx;
|
||||
}
|
||||
|
||||
public static MetaSwap? CreateEqp( Func< MetaManipulation, MetaManipulation > manips, EquipSlot slot, SetId idFrom, SetId idTo )
|
||||
public static MetaSwap? CreateEqp(MetaFileManager manager, Func<MetaManipulation, MetaManipulation> manips, EquipSlot slot, SetId idFrom,
|
||||
SetId idTo)
|
||||
{
|
||||
if( slot.IsAccessory() )
|
||||
{
|
||||
if (slot.IsAccessory())
|
||||
return null;
|
||||
}
|
||||
|
||||
var eqpValueFrom = ExpandedEqpFile.GetDefault( idFrom.Value );
|
||||
var eqpValueTo = ExpandedEqpFile.GetDefault( idTo.Value );
|
||||
var eqpFrom = new EqpManipulation( eqpValueFrom, slot, idFrom.Value );
|
||||
var eqpTo = new EqpManipulation( eqpValueTo, slot, idFrom.Value );
|
||||
return new MetaSwap( manips, eqpFrom, eqpTo );
|
||||
var eqpValueFrom = ExpandedEqpFile.GetDefault(manager, idFrom.Value);
|
||||
var eqpValueTo = ExpandedEqpFile.GetDefault(manager, idTo.Value);
|
||||
var eqpFrom = new EqpManipulation(eqpValueFrom, slot, idFrom.Value);
|
||||
var eqpTo = new EqpManipulation(eqpValueTo, slot, idFrom.Value);
|
||||
return new MetaSwap(manips, eqpFrom, eqpTo);
|
||||
}
|
||||
|
||||
public static FileSwap? CreateMtrl( Func< Utf8GamePath, FullPath > redirections, EquipSlot slot, SetId idFrom, SetId idTo, byte variantTo, ref string fileName,
|
||||
ref bool dataWasChanged )
|
||||
=> CreateMtrl( redirections, slot, slot, idFrom, idTo, variantTo, ref fileName, ref dataWasChanged );
|
||||
public static FileSwap? CreateMtrl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slot, SetId idFrom,
|
||||
SetId idTo, byte variantTo, ref string fileName,
|
||||
ref bool dataWasChanged)
|
||||
=> CreateMtrl(manager, redirections, slot, slot, idFrom, idTo, variantTo, ref fileName, ref dataWasChanged);
|
||||
|
||||
public static FileSwap? CreateMtrl( Func< Utf8GamePath, FullPath > redirections, EquipSlot slotFrom, EquipSlot slotTo, SetId idFrom, SetId idTo, byte variantTo, ref string fileName,
|
||||
ref bool dataWasChanged )
|
||||
public static FileSwap? CreateMtrl(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, EquipSlot slotFrom, EquipSlot slotTo,
|
||||
SetId idFrom, SetId idTo, byte variantTo, ref string fileName,
|
||||
ref bool dataWasChanged)
|
||||
{
|
||||
var prefix = slotTo.IsAccessory() ? 'a' : 'e';
|
||||
if( !fileName.Contains( $"{prefix}{idTo.Value:D4}" ) )
|
||||
{
|
||||
if (!fileName.Contains($"{prefix}{idTo.Value:D4}"))
|
||||
return null;
|
||||
}
|
||||
|
||||
var folderTo = slotTo.IsAccessory() ? GamePaths.Accessory.Mtrl.FolderPath( idTo, variantTo ) : GamePaths.Equipment.Mtrl.FolderPath( idTo, variantTo );
|
||||
var pathTo = $"{folderTo}{fileName}";
|
||||
var folderTo = slotTo.IsAccessory()
|
||||
? GamePaths.Accessory.Mtrl.FolderPath(idTo, variantTo)
|
||||
: GamePaths.Equipment.Mtrl.FolderPath(idTo, variantTo);
|
||||
var pathTo = $"{folderTo}{fileName}";
|
||||
|
||||
var folderFrom = slotFrom.IsAccessory() ? GamePaths.Accessory.Mtrl.FolderPath( idFrom, variantTo ) : GamePaths.Equipment.Mtrl.FolderPath( idFrom, variantTo );
|
||||
var newFileName = ItemSwap.ReplaceId( fileName, prefix, idTo, idFrom );
|
||||
newFileName = ItemSwap.ReplaceSlot( newFileName, slotTo, slotFrom, slotTo != slotFrom );
|
||||
var pathFrom = $"{folderFrom}{newFileName}";
|
||||
var folderFrom = slotFrom.IsAccessory()
|
||||
? GamePaths.Accessory.Mtrl.FolderPath(idFrom, variantTo)
|
||||
: GamePaths.Equipment.Mtrl.FolderPath(idFrom, variantTo);
|
||||
var newFileName = ItemSwap.ReplaceId(fileName, prefix, idTo, idFrom);
|
||||
newFileName = ItemSwap.ReplaceSlot(newFileName, slotTo, slotFrom, slotTo != slotFrom);
|
||||
var pathFrom = $"{folderFrom}{newFileName}";
|
||||
|
||||
if( newFileName != fileName )
|
||||
if (newFileName != fileName)
|
||||
{
|
||||
fileName = newFileName;
|
||||
dataWasChanged = true;
|
||||
}
|
||||
|
||||
var mtrl = FileSwap.CreateSwap( ResourceType.Mtrl, redirections, pathFrom, pathTo );
|
||||
var shpk = CreateShader( redirections, ref mtrl.AsMtrl()!.ShaderPackage.Name, ref mtrl.DataWasChanged );
|
||||
mtrl.ChildSwaps.Add( shpk );
|
||||
var mtrl = FileSwap.CreateSwap(manager, ResourceType.Mtrl, redirections, pathFrom, pathTo);
|
||||
var shpk = CreateShader(manager, redirections, ref mtrl.AsMtrl()!.ShaderPackage.Name, ref mtrl.DataWasChanged);
|
||||
mtrl.ChildSwaps.Add(shpk);
|
||||
|
||||
foreach( ref var texture in mtrl.AsMtrl()!.Textures.AsSpan() )
|
||||
foreach (ref var texture in mtrl.AsMtrl()!.Textures.AsSpan())
|
||||
{
|
||||
var tex = CreateTex( redirections, prefix, slotFrom, slotTo, idFrom, idTo, ref texture, ref mtrl.DataWasChanged );
|
||||
mtrl.ChildSwaps.Add( tex );
|
||||
var tex = CreateTex(manager, redirections, prefix, slotFrom, slotTo, idFrom, idTo, ref texture, ref mtrl.DataWasChanged);
|
||||
mtrl.ChildSwaps.Add(tex);
|
||||
}
|
||||
|
||||
return mtrl;
|
||||
}
|
||||
|
||||
public static FileSwap CreateTex( Func< Utf8GamePath, FullPath > redirections, char prefix, SetId idFrom, SetId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged )
|
||||
=> CreateTex( redirections, prefix, EquipSlot.Unknown, EquipSlot.Unknown, idFrom, idTo, ref texture, ref dataWasChanged );
|
||||
public static FileSwap CreateTex(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, char prefix, SetId idFrom, SetId idTo,
|
||||
ref MtrlFile.Texture texture, ref bool dataWasChanged)
|
||||
=> CreateTex(manager, redirections, prefix, EquipSlot.Unknown, EquipSlot.Unknown, idFrom, idTo, ref texture, ref dataWasChanged);
|
||||
|
||||
public static FileSwap CreateTex( Func< Utf8GamePath, FullPath > redirections, char prefix, EquipSlot slotFrom, EquipSlot slotTo, SetId idFrom, SetId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged )
|
||||
public static FileSwap CreateTex(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, char prefix, EquipSlot slotFrom, EquipSlot slotTo, SetId idFrom,
|
||||
SetId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged)
|
||||
{
|
||||
var path = texture.Path;
|
||||
var addedDashes = false;
|
||||
if( texture.DX11 )
|
||||
if (texture.DX11)
|
||||
{
|
||||
var fileName = Path.GetFileName( path );
|
||||
if( !fileName.StartsWith( "--" ) )
|
||||
var fileName = Path.GetFileName(path);
|
||||
if (!fileName.StartsWith("--"))
|
||||
{
|
||||
path = path.Replace( fileName, $"--{fileName}" );
|
||||
path = path.Replace(fileName, $"--{fileName}");
|
||||
addedDashes = true;
|
||||
}
|
||||
}
|
||||
|
||||
var newPath = ItemSwap.ReplaceAnyId( path, prefix, idFrom );
|
||||
newPath = ItemSwap.ReplaceSlot( newPath, slotTo, slotFrom, slotTo != slotFrom );
|
||||
newPath = ItemSwap.AddSuffix( newPath, ".tex", $"_{Path.GetFileName( texture.Path ).GetStableHashCode():x8}" );
|
||||
if( newPath != path )
|
||||
var newPath = ItemSwap.ReplaceAnyId(path, prefix, idFrom);
|
||||
newPath = ItemSwap.ReplaceSlot(newPath, slotTo, slotFrom, slotTo != slotFrom);
|
||||
newPath = ItemSwap.AddSuffix(newPath, ".tex", $"_{Path.GetFileName(texture.Path).GetStableHashCode():x8}");
|
||||
if (newPath != path)
|
||||
{
|
||||
texture.Path = addedDashes ? newPath.Replace( "--", string.Empty ) : newPath;
|
||||
texture.Path = addedDashes ? newPath.Replace("--", string.Empty) : newPath;
|
||||
dataWasChanged = true;
|
||||
}
|
||||
|
||||
return FileSwap.CreateSwap( ResourceType.Tex, redirections, newPath, path, path );
|
||||
return FileSwap.CreateSwap(manager, ResourceType.Tex, redirections, newPath, path, path);
|
||||
}
|
||||
|
||||
public static FileSwap CreateShader( Func< Utf8GamePath, FullPath > redirections, ref string shaderName, ref bool dataWasChanged )
|
||||
public static FileSwap CreateShader(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, ref string shaderName, ref bool dataWasChanged)
|
||||
{
|
||||
var path = $"shader/sm5/shpk/{shaderName}";
|
||||
return FileSwap.CreateSwap( ResourceType.Shpk, redirections, path, path );
|
||||
return FileSwap.CreateSwap(manager, ResourceType.Shpk, redirections, path, path);
|
||||
}
|
||||
|
||||
public static FileSwap CreateAtex( Func< Utf8GamePath, FullPath > redirections, ref string filePath, ref bool dataWasChanged )
|
||||
public static FileSwap CreateAtex(MetaFileManager manager, Func<Utf8GamePath, FullPath> redirections, ref string filePath, ref bool dataWasChanged)
|
||||
{
|
||||
var oldPath = filePath;
|
||||
filePath = ItemSwap.AddSuffix( filePath, ".atex", $"_{Path.GetFileName( filePath ).GetStableHashCode():x8}" );
|
||||
filePath = ItemSwap.AddSuffix(filePath, ".atex", $"_{Path.GetFileName(filePath).GetStableHashCode():x8}");
|
||||
dataWasChanged = true;
|
||||
|
||||
return FileSwap.CreateSwap( ResourceType.Atex, redirections, filePath, oldPath, oldPath );
|
||||
return FileSwap.CreateSwap(manager, ResourceType.Atex, redirections, filePath, oldPath, oldPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Penumbra.GameData.Data;
|
|||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Files;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Services;
|
||||
|
|
@ -27,7 +28,7 @@ public static class ItemSwap
|
|||
=> Type = type;
|
||||
}
|
||||
|
||||
private static bool LoadFile( FullPath path, out byte[] data )
|
||||
private static bool LoadFile( MetaFileManager manager, FullPath path, out byte[] data )
|
||||
{
|
||||
if( path.FullName.Length > 0 )
|
||||
{
|
||||
|
|
@ -39,7 +40,7 @@ public static class ItemSwap
|
|||
return true;
|
||||
}
|
||||
|
||||
var file = DalamudServices.SGameData.GetFile( path.InternalName.ToString() );
|
||||
var file = manager.GameData.GetFile( path.InternalName.ToString() );
|
||||
if( file != null )
|
||||
{
|
||||
data = file.Data;
|
||||
|
|
@ -61,18 +62,18 @@ public static class ItemSwap
|
|||
public readonly byte[] Data;
|
||||
public bool Valid { get; }
|
||||
|
||||
public GenericFile( FullPath path )
|
||||
=> Valid = LoadFile( path, out Data );
|
||||
public GenericFile( MetaFileManager manager, FullPath path )
|
||||
=> Valid = LoadFile( manager, path, out Data );
|
||||
|
||||
public byte[] Write()
|
||||
=> Data;
|
||||
|
||||
public static readonly GenericFile Invalid = new(FullPath.Empty);
|
||||
public static readonly GenericFile Invalid = new(null!, FullPath.Empty);
|
||||
}
|
||||
|
||||
public static bool LoadFile( FullPath path, [NotNullWhen( true )] out GenericFile? file )
|
||||
public static bool LoadFile( MetaFileManager manager, FullPath path, [NotNullWhen( true )] out GenericFile? file )
|
||||
{
|
||||
file = new GenericFile( path );
|
||||
file = new GenericFile( manager, path );
|
||||
if( file.Valid )
|
||||
{
|
||||
return true;
|
||||
|
|
@ -82,11 +83,11 @@ public static class ItemSwap
|
|||
return false;
|
||||
}
|
||||
|
||||
public static bool LoadMdl( FullPath path, [NotNullWhen( true )] out MdlFile? file )
|
||||
public static bool LoadMdl( MetaFileManager manager, FullPath path, [NotNullWhen( true )] out MdlFile? file )
|
||||
{
|
||||
try
|
||||
{
|
||||
if( LoadFile( path, out byte[] data ) )
|
||||
if( LoadFile( manager, path, out byte[] data ) )
|
||||
{
|
||||
file = new MdlFile( data );
|
||||
return true;
|
||||
|
|
@ -101,11 +102,11 @@ public static class ItemSwap
|
|||
return false;
|
||||
}
|
||||
|
||||
public static bool LoadMtrl( FullPath path, [NotNullWhen( true )] out MtrlFile? file )
|
||||
public static bool LoadMtrl(MetaFileManager manager, FullPath path, [NotNullWhen( true )] out MtrlFile? file )
|
||||
{
|
||||
try
|
||||
{
|
||||
if( LoadFile( path, out byte[] data ) )
|
||||
if( LoadFile( manager, path, out byte[] data ) )
|
||||
{
|
||||
file = new MtrlFile( data );
|
||||
return true;
|
||||
|
|
@ -120,11 +121,11 @@ public static class ItemSwap
|
|||
return false;
|
||||
}
|
||||
|
||||
public static bool LoadAvfx( FullPath path, [NotNullWhen( true )] out AvfxFile? file )
|
||||
public static bool LoadAvfx( MetaFileManager manager, FullPath path, [NotNullWhen( true )] out AvfxFile? file )
|
||||
{
|
||||
try
|
||||
{
|
||||
if( LoadFile( path, out byte[] data ) )
|
||||
if( LoadFile( manager, path, out byte[] data ) )
|
||||
{
|
||||
file = new AvfxFile( data );
|
||||
return true;
|
||||
|
|
@ -140,20 +141,20 @@ public static class ItemSwap
|
|||
}
|
||||
|
||||
|
||||
public static FileSwap CreatePhyb( Func< Utf8GamePath, FullPath > redirections, EstManipulation.EstType type, GenderRace race, ushort estEntry )
|
||||
public static FileSwap CreatePhyb(MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, EstManipulation.EstType type, GenderRace race, ushort estEntry )
|
||||
{
|
||||
var phybPath = GamePaths.Skeleton.Phyb.Path( race, EstManipulation.ToName( type ), estEntry );
|
||||
return FileSwap.CreateSwap( ResourceType.Phyb, redirections, phybPath, phybPath );
|
||||
return FileSwap.CreateSwap( manager, ResourceType.Phyb, redirections, phybPath, phybPath );
|
||||
}
|
||||
|
||||
public static FileSwap CreateSklb( Func< Utf8GamePath, FullPath > redirections, EstManipulation.EstType type, GenderRace race, ushort estEntry )
|
||||
public static FileSwap CreateSklb(MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, EstManipulation.EstType type, GenderRace race, ushort estEntry )
|
||||
{
|
||||
var sklbPath = GamePaths.Skeleton.Sklb.Path( race, EstManipulation.ToName( type ), estEntry );
|
||||
return FileSwap.CreateSwap( ResourceType.Sklb, redirections, sklbPath, sklbPath );
|
||||
return FileSwap.CreateSwap(manager, ResourceType.Sklb, redirections, sklbPath, sklbPath );
|
||||
}
|
||||
|
||||
/// <remarks> metaChanges is not manipulated, but IReadOnlySet does not support TryGetValue. </remarks>
|
||||
public static MetaSwap? CreateEst( Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips, EstManipulation.EstType type,
|
||||
public static MetaSwap? CreateEst( MetaFileManager manager, Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips, EstManipulation.EstType type,
|
||||
GenderRace genderRace, SetId idFrom, SetId idTo, bool ownMdl )
|
||||
{
|
||||
if( type == 0 )
|
||||
|
|
@ -162,15 +163,15 @@ public static class ItemSwap
|
|||
}
|
||||
|
||||
var (gender, race) = genderRace.Split();
|
||||
var fromDefault = new EstManipulation( gender, race, type, idFrom.Value, EstFile.GetDefault( type, genderRace, idFrom.Value ) );
|
||||
var toDefault = new EstManipulation( gender, race, type, idTo.Value, EstFile.GetDefault( type, genderRace, idTo.Value ) );
|
||||
var fromDefault = new EstManipulation( gender, race, type, idFrom.Value, EstFile.GetDefault( manager, type, genderRace, idFrom.Value ) );
|
||||
var toDefault = new EstManipulation( gender, race, type, idTo.Value, EstFile.GetDefault( manager, type, genderRace, idTo.Value ) );
|
||||
var est = new MetaSwap( manips, fromDefault, toDefault );
|
||||
|
||||
if( ownMdl && est.SwapApplied.Est.Entry >= 2 )
|
||||
{
|
||||
var phyb = CreatePhyb( redirections, type, genderRace, est.SwapApplied.Est.Entry );
|
||||
var phyb = CreatePhyb( manager, redirections, type, genderRace, est.SwapApplied.Est.Entry );
|
||||
est.ChildSwaps.Add( phyb );
|
||||
var sklb = CreateSklb( redirections, type, genderRace, est.SwapApplied.Est.Entry );
|
||||
var sklb = CreateSklb( manager, redirections, type, genderRace, est.SwapApplied.Est.Entry );
|
||||
est.ChildSwaps.Add( sklb );
|
||||
}
|
||||
else if( est.SwapAppliedIsDefault )
|
||||
|
|
|
|||
|
|
@ -9,12 +9,14 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Mods.Manager;
|
||||
|
||||
namespace Penumbra.Mods.ItemSwap;
|
||||
|
||||
public class ItemSwapContainer
|
||||
{
|
||||
private readonly MetaFileManager _manager;
|
||||
private readonly IObjectIdentifier _identifier;
|
||||
|
||||
private Dictionary< Utf8GamePath, FullPath > _modRedirections = new();
|
||||
|
|
@ -112,8 +114,9 @@ public class ItemSwapContainer
|
|||
}
|
||||
}
|
||||
|
||||
public ItemSwapContainer(IObjectIdentifier identifier)
|
||||
public ItemSwapContainer(MetaFileManager manager, IObjectIdentifier identifier)
|
||||
{
|
||||
_manager = manager;
|
||||
_identifier = identifier;
|
||||
LoadMod( null, null );
|
||||
}
|
||||
|
|
@ -133,7 +136,7 @@ public class ItemSwapContainer
|
|||
{
|
||||
Swaps.Clear();
|
||||
Loaded = false;
|
||||
var ret = EquipmentSwap.CreateItemSwap( _identifier, Swaps, PathResolver( collection ), MetaResolver( collection ), from, to, useRightRing, useLeftRing );
|
||||
var ret = EquipmentSwap.CreateItemSwap( _manager, _identifier, Swaps, PathResolver( collection ), MetaResolver( collection ), from, to, useRightRing, useLeftRing );
|
||||
Loaded = true;
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -142,15 +145,15 @@ public class ItemSwapContainer
|
|||
{
|
||||
Swaps.Clear();
|
||||
Loaded = false;
|
||||
var ret = EquipmentSwap.CreateTypeSwap( _identifier, Swaps, PathResolver( collection ), MetaResolver( collection ), slotFrom, from, slotTo, to );
|
||||
var ret = EquipmentSwap.CreateTypeSwap( _manager, _identifier, Swaps, PathResolver( collection ), MetaResolver( collection ), slotFrom, from, slotTo, to );
|
||||
Loaded = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool LoadCustomization( BodySlot slot, GenderRace race, SetId from, SetId to, ModCollection? collection = null )
|
||||
public bool LoadCustomization( MetaFileManager manager, BodySlot slot, GenderRace race, SetId from, SetId to, ModCollection? collection = null )
|
||||
{
|
||||
var pathResolver = PathResolver( collection );
|
||||
var mdl = CustomizationSwap.CreateMdl( pathResolver, slot, race, from, to );
|
||||
var mdl = CustomizationSwap.CreateMdl( manager, pathResolver, slot, race, from, to );
|
||||
var type = slot switch
|
||||
{
|
||||
BodySlot.Hair => EstManipulation.EstType.Hair,
|
||||
|
|
@ -159,7 +162,7 @@ public class ItemSwapContainer
|
|||
};
|
||||
|
||||
var metaResolver = MetaResolver( collection );
|
||||
var est = ItemSwap.CreateEst( pathResolver, metaResolver, type, race, from, to, true );
|
||||
var est = ItemSwap.CreateEst( manager, pathResolver, metaResolver, type, race, from, to, true );
|
||||
|
||||
Swaps.Add( mdl );
|
||||
if( est != null )
|
||||
|
|
|
|||
|
|
@ -7,18 +7,19 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Meta;
|
||||
using static Penumbra.Mods.ItemSwap.ItemSwap;
|
||||
using Penumbra.Services;
|
||||
|
||||
using Penumbra.Services;
|
||||
|
||||
namespace Penumbra.Mods.ItemSwap;
|
||||
|
||||
public class Swap
|
||||
{
|
||||
/// <summary> Any further swaps belonging specifically to this tree of changes. </summary>
|
||||
public readonly List< Swap > ChildSwaps = new();
|
||||
public readonly List<Swap> ChildSwaps = new();
|
||||
|
||||
public IEnumerable< Swap > WithChildren()
|
||||
=> ChildSwaps.SelectMany( c => c.WithChildren() ).Prepend( this );
|
||||
public IEnumerable<Swap> WithChildren()
|
||||
=> ChildSwaps.SelectMany(c => c.WithChildren()).Prepend(this);
|
||||
}
|
||||
|
||||
public sealed class MetaSwap : Swap
|
||||
|
|
@ -47,15 +48,15 @@ public sealed class MetaSwap : Swap
|
|||
/// <param name="manipulations">A function that converts the given manipulation to the modded one.</param>
|
||||
/// <param name="manipFrom">The original meta identifier with its default value.</param>
|
||||
/// <param name="manipTo">The target meta identifier with its default value.</param>
|
||||
public MetaSwap( Func< MetaManipulation, MetaManipulation > manipulations, MetaManipulation manipFrom, MetaManipulation manipTo )
|
||||
public MetaSwap(Func<MetaManipulation, MetaManipulation> manipulations, MetaManipulation manipFrom, MetaManipulation manipTo)
|
||||
{
|
||||
SwapFrom = manipFrom;
|
||||
SwapToDefault = manipTo;
|
||||
|
||||
SwapToModded = manipulations( manipTo );
|
||||
SwapToIsDefault = manipTo.EntryEquals( SwapToModded );
|
||||
SwapApplied = SwapFrom.WithEntryOf( SwapToModded );
|
||||
SwapAppliedIsDefault = SwapApplied.EntryEquals( SwapFrom );
|
||||
SwapToModded = manipulations(manipTo);
|
||||
SwapToIsDefault = manipTo.EntryEquals(SwapToModded);
|
||||
SwapApplied = SwapFrom.WithEntryOf(SwapToModded);
|
||||
SwapAppliedIsDefault = SwapApplied.EntryEquals(SwapFrom);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,8 +96,8 @@ public sealed class FileSwap : Swap
|
|||
/// <summary> Whether SwapFromPreChangePath equals SwapFromRequest. </summary>
|
||||
public bool SwapFromChanged;
|
||||
|
||||
public string GetNewPath( string newMod )
|
||||
=> Path.Combine( newMod, new Utf8RelPath( SwapFromRequestPath ).ToString() );
|
||||
public string GetNewPath(string newMod)
|
||||
=> Path.Combine(newMod, new Utf8RelPath(SwapFromRequestPath).ToString());
|
||||
|
||||
public MdlFile? AsMdl()
|
||||
=> FileData as MdlFile;
|
||||
|
|
@ -116,8 +117,9 @@ public sealed class FileSwap : Swap
|
|||
/// <param name="swapToRequest">The unmodded path to the file the game is supposed to load instead.</param>
|
||||
/// <param name="swap">A full swap container with the actual file in memory.</param>
|
||||
/// <returns>True if everything could be read correctly, false otherwise.</returns>
|
||||
public static FileSwap CreateSwap( ResourceType type, Func< Utf8GamePath, FullPath > redirections, string swapFromRequest, string swapToRequest,
|
||||
string? swapFromPreChange = null )
|
||||
public static FileSwap CreateSwap(MetaFileManager manager, ResourceType type, Func<Utf8GamePath, FullPath> redirections,
|
||||
string swapFromRequest, string swapToRequest,
|
||||
string? swapFromPreChange = null)
|
||||
{
|
||||
var swap = new FileSwap
|
||||
{
|
||||
|
|
@ -131,49 +133,25 @@ public sealed class FileSwap : Swap
|
|||
SwapToModded = FullPath.Empty,
|
||||
};
|
||||
|
||||
if( swapFromRequest.Length == 0
|
||||
|| swapToRequest.Length == 0
|
||||
|| !Utf8GamePath.FromString( swapToRequest, out swap.SwapToRequestPath )
|
||||
|| !Utf8GamePath.FromString( swapFromRequest, out swap.SwapFromRequestPath ) )
|
||||
{
|
||||
throw new Exception( $"Could not create UTF8 String for \"{swapFromRequest}\" or \"{swapToRequest}\"." );
|
||||
}
|
||||
if (swapFromRequest.Length == 0
|
||||
|| swapToRequest.Length == 0
|
||||
|| !Utf8GamePath.FromString(swapToRequest, out swap.SwapToRequestPath)
|
||||
|| !Utf8GamePath.FromString(swapFromRequest, out swap.SwapFromRequestPath))
|
||||
throw new Exception($"Could not create UTF8 String for \"{swapFromRequest}\" or \"{swapToRequest}\".");
|
||||
|
||||
swap.SwapToModded = redirections( swap.SwapToRequestPath );
|
||||
swap.SwapToModdedExistsInGame = !swap.SwapToModded.IsRooted && DalamudServices.SGameData.FileExists( swap.SwapToModded.InternalName.ToString() );
|
||||
swap.SwapToModdedEqualsOriginal = !swap.SwapToModded.IsRooted && swap.SwapToModded.InternalName.Equals( swap.SwapFromRequestPath.Path );
|
||||
swap.SwapToModded = redirections(swap.SwapToRequestPath);
|
||||
swap.SwapToModdedExistsInGame =
|
||||
!swap.SwapToModded.IsRooted && DalamudServices.SGameData.FileExists(swap.SwapToModded.InternalName.ToString());
|
||||
swap.SwapToModdedEqualsOriginal = !swap.SwapToModded.IsRooted && swap.SwapToModded.InternalName.Equals(swap.SwapFromRequestPath.Path);
|
||||
|
||||
swap.FileData = type switch
|
||||
{
|
||||
ResourceType.Mdl => LoadMdl( swap.SwapToModded, out var f ) ? f : throw new MissingFileException( type, swap.SwapToModded ),
|
||||
ResourceType.Mtrl => LoadMtrl( swap.SwapToModded, out var f ) ? f : throw new MissingFileException( type, swap.SwapToModded ),
|
||||
ResourceType.Avfx => LoadAvfx( swap.SwapToModded, out var f ) ? f : throw new MissingFileException( type, swap.SwapToModded ),
|
||||
_ => LoadFile( swap.SwapToModded, out var f ) ? f : throw new MissingFileException( type, swap.SwapToModded ),
|
||||
ResourceType.Mdl => LoadMdl(manager, swap.SwapToModded, out var f) ? f : throw new MissingFileException(type, swap.SwapToModded),
|
||||
ResourceType.Mtrl => LoadMtrl(manager, swap.SwapToModded, out var f) ? f : throw new MissingFileException(type, swap.SwapToModded),
|
||||
ResourceType.Avfx => LoadAvfx(manager, swap.SwapToModded, out var f) ? f : throw new MissingFileException(type, swap.SwapToModded),
|
||||
_ => LoadFile(manager, swap.SwapToModded, out var f) ? f : throw new MissingFileException(type, swap.SwapToModded),
|
||||
};
|
||||
|
||||
return swap;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Convert a single file redirection to use the file name and extension given by type and the files SHA256 hash, if possible.
|
||||
/// </summary>
|
||||
/// <param name="redirections">The set of redirections that need to be considered.</param>
|
||||
/// <param name="path">The in- and output path for a file</param>
|
||||
/// <param name="dataWasChanged">Will be set to true if <paramref name="path"/> was changed.</param>
|
||||
/// <param name="swap">Will be updated.</param>
|
||||
public static bool CreateShaRedirection( Func< Utf8GamePath, FullPath > redirections, ref string path, ref bool dataWasChanged, ref FileSwap swap )
|
||||
{
|
||||
var oldFilename = Path.GetFileName( path );
|
||||
var hash = SHA256.HashData( swap.FileData.Write() );
|
||||
var name =
|
||||
$"{( oldFilename.StartsWith( "--" ) ? "--" : string.Empty )}{string.Join( null, hash.Select( c => c.ToString( "x2" ) ) )}.{swap.Type.ToString().ToLowerInvariant()}";
|
||||
var newPath = path.Replace( oldFilename, name );
|
||||
var newSwap = CreateSwap( swap.Type, redirections, newPath, swap.SwapToRequestPath.ToString() );
|
||||
|
||||
path = newPath;
|
||||
dataWasChanged = true;
|
||||
swap = newSwap;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
|
|
@ -109,11 +110,11 @@ public partial class Mod
|
|||
}
|
||||
}
|
||||
|
||||
public void WriteAllTexToolsMeta()
|
||||
public void WriteAllTexToolsMeta(MetaFileManager manager)
|
||||
{
|
||||
try
|
||||
{
|
||||
_default.WriteTexToolsMeta(ModPath);
|
||||
_default.WriteTexToolsMeta(manager, ModPath);
|
||||
foreach (var group in Groups)
|
||||
{
|
||||
var dir = ModCreator.NewOptionDirectory(ModPath, group.Name);
|
||||
|
|
@ -126,7 +127,7 @@ public partial class Mod
|
|||
if (!optionDir.Exists)
|
||||
optionDir.Create();
|
||||
|
||||
option.WriteTexToolsMeta(optionDir);
|
||||
option.WriteTexToolsMeta(manager, optionDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.Import;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// A sub mod is a collection of
|
||||
/// - file replacements
|
||||
/// - file swaps
|
||||
|
|
@ -18,14 +19,14 @@ namespace Penumbra.Mods;
|
|||
/// that can be used either as an option or as the default data for a mod.
|
||||
/// It can be loaded and reloaded from Json.
|
||||
/// Nothing is checked for existence or validity when loading.
|
||||
/// Objects are also not checked for uniqueness, the first appearance of a game path or meta path decides.
|
||||
/// Objects are also not checked for uniqueness, the first appearance of a game path or meta path decides.
|
||||
/// </summary>
|
||||
public sealed class SubMod : ISubMod
|
||||
public sealed class SubMod : ISubMod
|
||||
{
|
||||
public string Name { get; set; } = "Default";
|
||||
|
||||
public string FullName
|
||||
=> GroupIdx < 0 ? "Default Option" : $"{ParentMod.Groups[ GroupIdx ].Name}: {Name}";
|
||||
=> GroupIdx < 0 ? "Default Option" : $"{ParentMod.Groups[GroupIdx].Name}: {Name}";
|
||||
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
|
|
@ -36,180 +37,165 @@ namespace Penumbra.Mods;
|
|||
public bool IsDefault
|
||||
=> GroupIdx < 0;
|
||||
|
||||
public Dictionary< Utf8GamePath, FullPath > FileData = new();
|
||||
public Dictionary< Utf8GamePath, FullPath > FileSwapData = new();
|
||||
public HashSet< MetaManipulation > ManipulationData = new();
|
||||
public Dictionary<Utf8GamePath, FullPath> FileData = new();
|
||||
public Dictionary<Utf8GamePath, FullPath> FileSwapData = new();
|
||||
public HashSet<MetaManipulation> ManipulationData = new();
|
||||
|
||||
public SubMod( IMod parentMod )
|
||||
public SubMod(IMod parentMod)
|
||||
=> ParentMod = parentMod;
|
||||
|
||||
public IReadOnlyDictionary< Utf8GamePath, FullPath > Files
|
||||
public IReadOnlyDictionary<Utf8GamePath, FullPath> Files
|
||||
=> FileData;
|
||||
|
||||
public IReadOnlyDictionary< Utf8GamePath, FullPath > FileSwaps
|
||||
public IReadOnlyDictionary<Utf8GamePath, FullPath> FileSwaps
|
||||
=> FileSwapData;
|
||||
|
||||
public IReadOnlySet< MetaManipulation > Manipulations
|
||||
public IReadOnlySet<MetaManipulation> Manipulations
|
||||
=> ManipulationData;
|
||||
|
||||
public void SetPosition( int groupIdx, int optionIdx )
|
||||
public void SetPosition(int groupIdx, int optionIdx)
|
||||
{
|
||||
GroupIdx = groupIdx;
|
||||
OptionIdx = optionIdx;
|
||||
}
|
||||
|
||||
public void Load( DirectoryInfo basePath, JToken json, out int priority )
|
||||
public void Load(DirectoryInfo basePath, JToken json, out int priority)
|
||||
{
|
||||
FileData.Clear();
|
||||
FileSwapData.Clear();
|
||||
ManipulationData.Clear();
|
||||
|
||||
// Every option has a name, but priorities are only relevant for multi group options.
|
||||
Name = json[ nameof( ISubMod.Name ) ]?.ToObject< string >() ?? string.Empty;
|
||||
Description = json[ nameof( ISubMod.Description ) ]?.ToObject< string >() ?? string.Empty;
|
||||
priority = json[ nameof( IModGroup.Priority ) ]?.ToObject< int >() ?? 0;
|
||||
Name = json[nameof(ISubMod.Name)]?.ToObject<string>() ?? string.Empty;
|
||||
Description = json[nameof(ISubMod.Description)]?.ToObject<string>() ?? string.Empty;
|
||||
priority = json[nameof(IModGroup.Priority)]?.ToObject<int>() ?? 0;
|
||||
|
||||
var files = ( JObject? )json[ nameof( Files ) ];
|
||||
if( files != null )
|
||||
{
|
||||
foreach( var property in files.Properties() )
|
||||
var files = (JObject?)json[nameof(Files)];
|
||||
if (files != null)
|
||||
foreach (var property in files.Properties())
|
||||
{
|
||||
if( Utf8GamePath.FromString( property.Name, out var p, true ) )
|
||||
{
|
||||
FileData.TryAdd( p, new FullPath( basePath, property.Value.ToObject< Utf8RelPath >() ) );
|
||||
}
|
||||
if (Utf8GamePath.FromString(property.Name, out var p, true))
|
||||
FileData.TryAdd(p, new FullPath(basePath, property.Value.ToObject<Utf8RelPath>()));
|
||||
}
|
||||
}
|
||||
|
||||
var swaps = ( JObject? )json[ nameof( FileSwaps ) ];
|
||||
if( swaps != null )
|
||||
{
|
||||
foreach( var property in swaps.Properties() )
|
||||
var swaps = (JObject?)json[nameof(FileSwaps)];
|
||||
if (swaps != null)
|
||||
foreach (var property in swaps.Properties())
|
||||
{
|
||||
if( Utf8GamePath.FromString( property.Name, out var p, true ) )
|
||||
{
|
||||
FileSwapData.TryAdd( p, new FullPath( property.Value.ToObject< string >()! ) );
|
||||
}
|
||||
if (Utf8GamePath.FromString(property.Name, out var p, true))
|
||||
FileSwapData.TryAdd(p, new FullPath(property.Value.ToObject<string>()!));
|
||||
}
|
||||
}
|
||||
|
||||
var manips = json[ nameof( Manipulations ) ];
|
||||
if( manips != null )
|
||||
{
|
||||
foreach( var s in manips.Children().Select( c => c.ToObject< MetaManipulation >() ).Where( m => m.ManipulationType != MetaManipulation.Type.Unknown ) )
|
||||
{
|
||||
ManipulationData.Add( s );
|
||||
}
|
||||
}
|
||||
var manips = json[nameof(Manipulations)];
|
||||
if (manips != null)
|
||||
foreach (var s in manips.Children().Select(c => c.ToObject<MetaManipulation>())
|
||||
.Where(m => m.ManipulationType != MetaManipulation.Type.Unknown))
|
||||
ManipulationData.Add(s);
|
||||
}
|
||||
|
||||
// If .meta or .rgsp files are encountered, parse them and incorporate their meta changes into the mod.
|
||||
// If delete is true, the files are deleted afterwards.
|
||||
public (bool Changes, List< string > DeleteList) IncorporateMetaChanges( DirectoryInfo basePath, bool delete )
|
||||
public (bool Changes, List<string> DeleteList) IncorporateMetaChanges(DirectoryInfo basePath, bool delete)
|
||||
{
|
||||
var deleteList = new List< string >();
|
||||
var deleteList = new List<string>();
|
||||
var oldSize = ManipulationData.Count;
|
||||
var deleteString = delete ? "with deletion." : "without deletion.";
|
||||
foreach( var (key, file) in Files.ToList() )
|
||||
foreach (var (key, file) in Files.ToList())
|
||||
{
|
||||
var ext1 = key.Extension().AsciiToLower().ToString();
|
||||
var ext2 = file.Extension.ToLowerInvariant();
|
||||
try
|
||||
{
|
||||
if( ext1 == ".meta" || ext2 == ".meta" )
|
||||
if (ext1 == ".meta" || ext2 == ".meta")
|
||||
{
|
||||
FileData.Remove( key );
|
||||
if( !file.Exists )
|
||||
{
|
||||
FileData.Remove(key);
|
||||
if (!file.Exists)
|
||||
continue;
|
||||
}
|
||||
|
||||
var meta = new TexToolsMeta( Penumbra.GamePathParser, File.ReadAllBytes( file.FullName ), Penumbra.Config.KeepDefaultMetaChanges );
|
||||
Penumbra.Log.Verbose( $"Incorporating {file} as Metadata file of {meta.MetaManipulations.Count} manipulations {deleteString}" );
|
||||
deleteList.Add( file.FullName );
|
||||
ManipulationData.UnionWith( meta.MetaManipulations );
|
||||
var meta = new TexToolsMeta(Penumbra.MetaFileManager, Penumbra.GamePathParser, File.ReadAllBytes(file.FullName),
|
||||
Penumbra.Config.KeepDefaultMetaChanges);
|
||||
Penumbra.Log.Verbose(
|
||||
$"Incorporating {file} as Metadata file of {meta.MetaManipulations.Count} manipulations {deleteString}");
|
||||
deleteList.Add(file.FullName);
|
||||
ManipulationData.UnionWith(meta.MetaManipulations);
|
||||
}
|
||||
else if( ext1 == ".rgsp" || ext2 == ".rgsp" )
|
||||
else if (ext1 == ".rgsp" || ext2 == ".rgsp")
|
||||
{
|
||||
FileData.Remove( key );
|
||||
if( !file.Exists )
|
||||
{
|
||||
FileData.Remove(key);
|
||||
if (!file.Exists)
|
||||
continue;
|
||||
}
|
||||
|
||||
var rgsp = TexToolsMeta.FromRgspFile( file.FullName, File.ReadAllBytes( file.FullName ), Penumbra.Config.KeepDefaultMetaChanges );
|
||||
Penumbra.Log.Verbose( $"Incorporating {file} as racial scaling file of {rgsp.MetaManipulations.Count} manipulations {deleteString}" );
|
||||
deleteList.Add( file.FullName );
|
||||
var rgsp = TexToolsMeta.FromRgspFile(Penumbra.MetaFileManager, file.FullName, File.ReadAllBytes(file.FullName),
|
||||
Penumbra.Config.KeepDefaultMetaChanges);
|
||||
Penumbra.Log.Verbose(
|
||||
$"Incorporating {file} as racial scaling file of {rgsp.MetaManipulations.Count} manipulations {deleteString}");
|
||||
deleteList.Add(file.FullName);
|
||||
|
||||
ManipulationData.UnionWith( rgsp.MetaManipulations );
|
||||
ManipulationData.UnionWith(rgsp.MetaManipulations);
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error( $"Could not incorporate meta changes in mod {basePath} from file {file.FullName}:\n{e}" );
|
||||
Penumbra.Log.Error($"Could not incorporate meta changes in mod {basePath} from file {file.FullName}:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
DeleteDeleteList( deleteList, delete );
|
||||
return ( oldSize < ManipulationData.Count, deleteList );
|
||||
DeleteDeleteList(deleteList, delete);
|
||||
return (oldSize < ManipulationData.Count, deleteList);
|
||||
}
|
||||
|
||||
internal static void DeleteDeleteList( IEnumerable< string > deleteList, bool delete )
|
||||
internal static void DeleteDeleteList(IEnumerable<string> deleteList, bool delete)
|
||||
{
|
||||
if( !delete )
|
||||
{
|
||||
if (!delete)
|
||||
return;
|
||||
}
|
||||
|
||||
foreach( var file in deleteList )
|
||||
foreach (var file in deleteList)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete( file );
|
||||
File.Delete(file);
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error( $"Could not delete incorporated meta file {file}:\n{e}" );
|
||||
Penumbra.Log.Error($"Could not delete incorporated meta file {file}:\n{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteTexToolsMeta( DirectoryInfo basePath, bool test = false )
|
||||
public void WriteTexToolsMeta(MetaFileManager manager, DirectoryInfo basePath, bool test = false)
|
||||
{
|
||||
var files = TexToolsMeta.ConvertToTexTools( Manipulations );
|
||||
var files = TexToolsMeta.ConvertToTexTools(manager, Manipulations);
|
||||
|
||||
foreach( var (file, data) in files )
|
||||
foreach (var (file, data) in files)
|
||||
{
|
||||
var path = Path.Combine( basePath.FullName, file );
|
||||
var path = Path.Combine(basePath.FullName, file);
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory( Path.GetDirectoryName( path )! );
|
||||
File.WriteAllBytes( path, data );
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
||||
File.WriteAllBytes(path, data);
|
||||
}
|
||||
catch( Exception e )
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error( $"Could not write meta file {path}:\n{e}" );
|
||||
Penumbra.Log.Error($"Could not write meta file {path}:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
if( test )
|
||||
{
|
||||
TestMetaWriting( files );
|
||||
}
|
||||
if (test)
|
||||
TestMetaWriting(manager, files);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG" )]
|
||||
private void TestMetaWriting( Dictionary< string, byte[] > files )
|
||||
[Conditional("DEBUG")]
|
||||
private void TestMetaWriting(MetaFileManager manager, Dictionary<string, byte[]> files)
|
||||
{
|
||||
var meta = new HashSet< MetaManipulation >( Manipulations.Count );
|
||||
foreach( var (file, data) in files )
|
||||
var meta = new HashSet<MetaManipulation>(Manipulations.Count);
|
||||
foreach (var (file, data) in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
var x = file.EndsWith( "rgsp" )
|
||||
? TexToolsMeta.FromRgspFile( file, data, Penumbra.Config.KeepDefaultMetaChanges )
|
||||
: new TexToolsMeta( Penumbra.GamePathParser, data, Penumbra.Config.KeepDefaultMetaChanges );
|
||||
meta.UnionWith( x.MetaManipulations );
|
||||
var x = file.EndsWith("rgsp")
|
||||
? TexToolsMeta.FromRgspFile(manager, file, data, Penumbra.Config.KeepDefaultMetaChanges)
|
||||
: new TexToolsMeta(manager, Penumbra.GamePathParser, data, Penumbra.Config.KeepDefaultMetaChanges);
|
||||
meta.UnionWith(x.MetaManipulations);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
@ -217,27 +203,21 @@ namespace Penumbra.Mods;
|
|||
}
|
||||
}
|
||||
|
||||
if( !Manipulations.SetEquals( meta ) )
|
||||
if (!Manipulations.SetEquals(meta))
|
||||
{
|
||||
Penumbra.Log.Information( "Meta Sets do not equal." );
|
||||
foreach( var (m1, m2) in Manipulations.Zip( meta ) )
|
||||
{
|
||||
Penumbra.Log.Information( $"{m1} {m1.EntryToString()} | {m2} {m2.EntryToString()}" );
|
||||
}
|
||||
Penumbra.Log.Information("Meta Sets do not equal.");
|
||||
foreach (var (m1, m2) in Manipulations.Zip(meta))
|
||||
Penumbra.Log.Information($"{m1} {m1.EntryToString()} | {m2} {m2.EntryToString()}");
|
||||
|
||||
foreach( var m in Manipulations.Skip( meta.Count ) )
|
||||
{
|
||||
Penumbra.Log.Information( $"{m} {m.EntryToString()} " );
|
||||
}
|
||||
foreach (var m in Manipulations.Skip(meta.Count))
|
||||
Penumbra.Log.Information($"{m} {m.EntryToString()} ");
|
||||
|
||||
foreach( var m in meta.Skip( Manipulations.Count ) )
|
||||
{
|
||||
Penumbra.Log.Information( $"{m} {m.EntryToString()} " );
|
||||
}
|
||||
foreach (var m in meta.Skip(Manipulations.Count))
|
||||
Penumbra.Log.Information($"{m} {m.EntryToString()} ");
|
||||
}
|
||||
else
|
||||
{
|
||||
Penumbra.Log.Information( "Meta Sets are equal." );
|
||||
Penumbra.Log.Information("Meta Sets are equal.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue