mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
tmp
This commit is contained in:
parent
46581780e0
commit
de082439a4
28 changed files with 766 additions and 835 deletions
|
|
@ -35,7 +35,6 @@ public class Configuration : IPluginConfiguration
|
|||
public int WaitFrames { get; set; } = 30;
|
||||
|
||||
public string ModDirectory { get; set; } = string.Empty;
|
||||
public string TempDirectory { get; set; } = string.Empty;
|
||||
|
||||
public string CurrentCollection { get; set; } = "Default";
|
||||
public string DefaultCollection { get; set; } = "Default";
|
||||
|
|
|
|||
|
|
@ -256,11 +256,10 @@ public class TexToolsMeta
|
|||
ushort i = 0;
|
||||
if( info.PrimaryType is ObjectType.Equipment or ObjectType.Accessory )
|
||||
{
|
||||
// TODO check against default.
|
||||
foreach( var value in values )
|
||||
{
|
||||
ImcEntry def;
|
||||
if( !value.Equals( def ) )
|
||||
ImcManipulations.Add(new ImcManipulation(info.EquipSlot, i, info.PrimaryId, value) );
|
||||
ImcManipulations.Add(new ImcManipulation(info.EquipSlot, i, info.PrimaryId, value) );
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
|
@ -268,55 +267,64 @@ public class TexToolsMeta
|
|||
{
|
||||
foreach( var value in values )
|
||||
{
|
||||
ImcEntry def;
|
||||
if( !value.Equals( def ) )
|
||||
ImcManipulations.Add( new ImcManipulation( info.PrimaryType, info.SecondaryType, info.PrimaryId, info.SecondaryId, i, value ) );
|
||||
ImcManipulations.Add( new ImcManipulation( info.PrimaryType, info.SecondaryType, info.PrimaryId, info.SecondaryId, i, value ) );
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string ReadNullTerminated( BinaryReader reader )
|
||||
{
|
||||
var builder = new System.Text.StringBuilder();
|
||||
for( var c = reader.ReadChar(); c != 0; c = reader.ReadChar() )
|
||||
{
|
||||
builder.Append( c );
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public TexToolsMeta( byte[] data )
|
||||
{
|
||||
try
|
||||
{
|
||||
//using var reader = new BinaryReader( new MemoryStream( data ) );
|
||||
//Version = reader.ReadUInt32();
|
||||
//FilePath = ReadNullTerminated( reader );
|
||||
//var metaInfo = new Info( FilePath );
|
||||
//var numHeaders = reader.ReadUInt32();
|
||||
//var headerSize = reader.ReadUInt32();
|
||||
//var headerStart = reader.ReadUInt32();
|
||||
//reader.BaseStream.Seek( headerStart, SeekOrigin.Begin );
|
||||
//
|
||||
//List< (MetaType type, uint offset, int size) > entries = new();
|
||||
//for( var i = 0; i < numHeaders; ++i )
|
||||
//{
|
||||
// var currentOffset = reader.BaseStream.Position;
|
||||
// var type = ( MetaType )reader.ReadUInt32();
|
||||
// var offset = reader.ReadUInt32();
|
||||
// var size = reader.ReadInt32();
|
||||
// entries.Add( ( type, offset, size ) );
|
||||
// reader.BaseStream.Seek( currentOffset + headerSize, SeekOrigin.Begin );
|
||||
//}
|
||||
//
|
||||
//byte[]? ReadEntry( MetaType type )
|
||||
//{
|
||||
// var idx = entries.FindIndex( t => t.type == type );
|
||||
// if( idx < 0 )
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// reader.BaseStream.Seek( entries[ idx ].offset, SeekOrigin.Begin );
|
||||
// return reader.ReadBytes( entries[ idx ].size );
|
||||
//}
|
||||
//
|
||||
//DeserializeEqpEntry( metaInfo, ReadEntry( MetaManipulation.Type.Eqp ) );
|
||||
//DeserializeGmpEntry( metaInfo, ReadEntry( MetaManipulation.Type.Gmp ) );
|
||||
//DeserializeEqdpEntries( metaInfo, ReadEntry( MetaManipulation.Type.Eqdp ) );
|
||||
//DeserializeEstEntries( metaInfo, ReadEntry( MetaManipulation.Type.Est ) );
|
||||
//DeserializeImcEntries( metaInfo, ReadEntry( MetaManipulation.Type.Imc ) );
|
||||
using var reader = new BinaryReader( new MemoryStream( data ) );
|
||||
Version = reader.ReadUInt32();
|
||||
FilePath = ReadNullTerminated( reader );
|
||||
var metaInfo = new Info( FilePath );
|
||||
var numHeaders = reader.ReadUInt32();
|
||||
var headerSize = reader.ReadUInt32();
|
||||
var headerStart = reader.ReadUInt32();
|
||||
reader.BaseStream.Seek( headerStart, SeekOrigin.Begin );
|
||||
|
||||
List< (MetaManipulation.Type type, uint offset, int size) > entries = new();
|
||||
for( var i = 0; i < numHeaders; ++i )
|
||||
{
|
||||
var currentOffset = reader.BaseStream.Position;
|
||||
var type = ( MetaManipulation.Type )reader.ReadUInt32();
|
||||
var offset = reader.ReadUInt32();
|
||||
var size = reader.ReadInt32();
|
||||
entries.Add( ( type, offset, size ) );
|
||||
reader.BaseStream.Seek( currentOffset + headerSize, SeekOrigin.Begin );
|
||||
}
|
||||
|
||||
byte[]? ReadEntry( MetaManipulation.Type type )
|
||||
{
|
||||
var idx = entries.FindIndex( t => t.type == type );
|
||||
if( idx < 0 )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
reader.BaseStream.Seek( entries[ idx ].offset, SeekOrigin.Begin );
|
||||
return reader.ReadBytes( entries[ idx ].size );
|
||||
}
|
||||
|
||||
DeserializeEqpEntry( metaInfo, ReadEntry( MetaManipulation.Type.Eqp ) );
|
||||
DeserializeGmpEntry( metaInfo, ReadEntry( MetaManipulation.Type.Gmp ) );
|
||||
DeserializeEqdpEntries( metaInfo, ReadEntry( MetaManipulation.Type.Eqdp ) );
|
||||
DeserializeEstEntries( metaInfo, ReadEntry( MetaManipulation.Type.Est ) );
|
||||
DeserializeImcEntries( metaInfo, ReadEntry( MetaManipulation.Type.Imc ) );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
|
|
@ -362,28 +370,35 @@ public class TexToolsMeta
|
|||
return Invalid;
|
||||
}
|
||||
|
||||
//if( gender == 1 )
|
||||
//{
|
||||
// ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.FemaleMinSize, br.ReadSingle() ) );
|
||||
// ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.FemaleMaxSize, br.ReadSingle() ) );
|
||||
// ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.FemaleMinTail, br.ReadSingle() ) );
|
||||
// ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.FemaleMaxTail, br.ReadSingle() ) );
|
||||
//
|
||||
// ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.BustMinX, br.ReadSingle() ) );
|
||||
// ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.BustMinY, br.ReadSingle() ) );
|
||||
// ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.BustMinZ, br.ReadSingle() ) );
|
||||
// ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.BustMaxX, br.ReadSingle() ) );
|
||||
// ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.BustMaxY, br.ReadSingle() ) );
|
||||
// ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.BustMaxZ, br.ReadSingle() ) );
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.MaleMinSize, br.ReadSingle() ) );
|
||||
// ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.MaleMaxSize, br.ReadSingle() ) );
|
||||
// ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.MaleMinTail, br.ReadSingle() ) );
|
||||
// ret.AddIfNotDefault( MetaManipulation.Rsp( subRace, RspAttribute.MaleMaxTail, br.ReadSingle() ) );
|
||||
//}
|
||||
//
|
||||
void Add( RspAttribute attribute, float value )
|
||||
{
|
||||
var def = CmpFile.GetDefault( subRace, attribute );
|
||||
if (value != def)
|
||||
ret!.RspManipulations.Add(new RspManipulation(subRace, attribute, value));
|
||||
}
|
||||
|
||||
if( gender == 1 )
|
||||
{
|
||||
Add(RspAttribute.FemaleMinSize, br.ReadSingle() );
|
||||
Add(RspAttribute.FemaleMaxSize, br.ReadSingle() );
|
||||
Add(RspAttribute.FemaleMinTail, br.ReadSingle() );
|
||||
Add(RspAttribute.FemaleMaxTail, br.ReadSingle() );
|
||||
|
||||
Add(RspAttribute.BustMinX, br.ReadSingle() );
|
||||
Add(RspAttribute.BustMinY, br.ReadSingle() );
|
||||
Add(RspAttribute.BustMinZ, br.ReadSingle() );
|
||||
Add(RspAttribute.BustMaxX, br.ReadSingle() );
|
||||
Add(RspAttribute.BustMaxY, br.ReadSingle() );
|
||||
Add(RspAttribute.BustMaxZ, br.ReadSingle() );
|
||||
}
|
||||
else
|
||||
{
|
||||
Add(RspAttribute.MaleMinSize, br.ReadSingle() );
|
||||
Add(RspAttribute.MaleMaxSize, br.ReadSingle() );
|
||||
Add(RspAttribute.MaleMinTail, br.ReadSingle() );
|
||||
Add(RspAttribute.MaleMaxTail, br.ReadSingle() );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -20,12 +20,20 @@ public unsafe class CharacterUtility : IDisposable
|
|||
public Structs.CharacterUtility* Address
|
||||
=> *_characterUtilityAddress;
|
||||
|
||||
public (IntPtr Address, int Size)[] DefaultResources = new (IntPtr, int)[Structs.CharacterUtility.NumResources];
|
||||
public (IntPtr Address, int Size)[] DefaultResources = new (IntPtr, int)[Structs.CharacterUtility.NumRelevantResources];
|
||||
|
||||
public CharacterUtility()
|
||||
{
|
||||
SignatureHelper.Initialise( this );
|
||||
LoadDataFilesHook.Enable();
|
||||
|
||||
if( Address->EqpResource != null )
|
||||
{
|
||||
LoadDefaultResources();
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadDataFilesHook.Enable();
|
||||
}
|
||||
}
|
||||
|
||||
// Self-disabling hook to set default resources after loading them.
|
||||
|
|
@ -40,7 +48,7 @@ public unsafe class CharacterUtility : IDisposable
|
|||
// We store the default data of the resources so we can always restore them.
|
||||
private void LoadDefaultResources()
|
||||
{
|
||||
for( var i = 0; i < Structs.CharacterUtility.NumResources; ++i )
|
||||
for( var i = 0; i < Structs.CharacterUtility.NumRelevantResources; ++i )
|
||||
{
|
||||
var resource = ( Structs.ResourceHandle* )Address->Resources[ i ];
|
||||
DefaultResources[ i ] = resource->GetData();
|
||||
|
|
@ -65,7 +73,7 @@ public unsafe class CharacterUtility : IDisposable
|
|||
|
||||
public void Dispose()
|
||||
{
|
||||
for( var i = 0; i < Structs.CharacterUtility.NumResources; ++i )
|
||||
for( var i = 0; i < Structs.CharacterUtility.NumRelevantResources; ++i )
|
||||
{
|
||||
ResetResource( i );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@ using Dalamud.Logging;
|
|||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.GameData.Util;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Util;
|
||||
using FileMode = Penumbra.Interop.Structs.FileMode;
|
||||
using ResourceHandle = FFXIVClientStructs.FFXIV.Client.System.Resource.Handle.ResourceHandle;
|
||||
|
||||
|
|
@ -123,6 +120,10 @@ public unsafe partial class ResourceLoader
|
|||
ret = ReadSqPackHook.Original( resourceManager, fileDescriptor, priority, isSync );
|
||||
FileLoaded?.Invoke( gamePath.Path, ret != 0, false );
|
||||
}
|
||||
else if( ResourceLoadCustomization != null && gamePath.Path[0] == (byte) '|' )
|
||||
{
|
||||
ret = ResourceLoadCustomization.Invoke( gamePath, resourceManager, fileDescriptor, priority, isSync );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Specify that we are loading unpacked files from the drive.
|
||||
|
|
@ -150,11 +151,18 @@ public unsafe partial class ResourceLoader
|
|||
return ret;
|
||||
}
|
||||
|
||||
// Customize file loading for any GamePaths that start with "|".
|
||||
public delegate byte ResourceLoadCustomizationDelegate( Utf8GamePath gamePath, ResourceManager* resourceManager,
|
||||
SeFileDescriptor* fileDescriptor, int priority, bool isSync );
|
||||
|
||||
public ResourceLoadCustomizationDelegate? ResourceLoadCustomization;
|
||||
|
||||
|
||||
// Use the default method of path replacement.
|
||||
public static (FullPath?, object?) DefaultReplacer( Utf8GamePath path )
|
||||
{
|
||||
var resolved = Penumbra.ModManager.ResolveSwappedOrReplacementPath( path );
|
||||
return( resolved, null );
|
||||
return ( resolved, null );
|
||||
}
|
||||
|
||||
private void DisposeHooks()
|
||||
|
|
|
|||
|
|
@ -7,15 +7,16 @@ namespace Penumbra.Interop.Structs;
|
|||
[StructLayout( LayoutKind.Explicit )]
|
||||
public unsafe struct CharacterUtility
|
||||
{
|
||||
public const int NumResources = 85;
|
||||
public const int EqpIdx = 0;
|
||||
public const int GmpIdx = 1;
|
||||
public const int HumanCmpIdx = 63;
|
||||
public const int FaceEstIdx = 64;
|
||||
public const int HairEstIdx = 65;
|
||||
public const int BodyEstIdx = 66;
|
||||
public const int HeadEstIdx = 67;
|
||||
public const int NumEqdpFiles = 2 * 28;
|
||||
public const int NumResources = 85;
|
||||
public const int NumRelevantResources = 68;
|
||||
public const int EqpIdx = 0;
|
||||
public const int GmpIdx = 1;
|
||||
public const int HumanCmpIdx = 63;
|
||||
public const int FaceEstIdx = 64;
|
||||
public const int HairEstIdx = 65;
|
||||
public const int BodyEstIdx = 66;
|
||||
public const int HeadEstIdx = 67;
|
||||
public const int NumEqdpFiles = 2 * 28;
|
||||
|
||||
public static int EqdpIdx( GenderRace raceCode, bool accessory )
|
||||
=> ( accessory ? 28 : 0 )
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
|
|||
{
|
||||
var ptr = ( byte* )DefaultData.Data;
|
||||
var controlBlock = *( ulong* )ptr;
|
||||
*( ulong* )ptr = ulong.MaxValue;
|
||||
*( ulong* )Data = ulong.MaxValue;
|
||||
for( var i = 0; i < 64; ++i )
|
||||
{
|
||||
var collapsed = ( ( controlBlock >> i ) & 1 ) == 0;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,21 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Memory;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Util;
|
||||
using Penumbra.Interop.Structs;
|
||||
|
||||
namespace Penumbra.Meta.Files;
|
||||
|
||||
public struct ImcEntry : IEquatable< ImcEntry >
|
||||
public readonly struct ImcEntry : IEquatable< ImcEntry >
|
||||
{
|
||||
public byte MaterialId;
|
||||
public byte DecalId;
|
||||
private ushort _attributeAndSound;
|
||||
public byte VfxId;
|
||||
public byte MaterialAnimationId;
|
||||
public readonly byte MaterialId;
|
||||
public readonly byte DecalId;
|
||||
private readonly ushort _attributeAndSound;
|
||||
public readonly byte VfxId;
|
||||
public readonly byte MaterialAnimationId;
|
||||
|
||||
public ushort AttributeMask
|
||||
=> ( ushort )( _attributeAndSound & 0x3FF );
|
||||
|
|
@ -163,4 +165,26 @@ public unsafe class ImcFile : MetaBaseFile
|
|||
Functions.MemCpyUnchecked( Data, ptr, file.Data.Length );
|
||||
}
|
||||
}
|
||||
|
||||
public void Replace( ResourceHandle* resource )
|
||||
{
|
||||
var (data, length) = resource->GetData();
|
||||
if( data == IntPtr.Zero )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var requiredLength = ActualLength;
|
||||
if( length >= requiredLength )
|
||||
{
|
||||
Functions.MemCpyUnchecked( ( void* )data, Data, requiredLength );
|
||||
Functions.MemSet( ( byte* )data + requiredLength, 0, length - requiredLength );
|
||||
return;
|
||||
}
|
||||
|
||||
MemoryHelper.GameFree( ref data, ( ulong )length );
|
||||
var file = ( byte* )MemoryHelper.GameAllocateDefault( ( ulong )requiredLength );
|
||||
Functions.MemCpyUnchecked( file, Data, requiredLength );
|
||||
resource->SetData( ( IntPtr )file, requiredLength );
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Memory;
|
||||
|
||||
namespace Penumbra.Meta.Files;
|
||||
|
||||
|
|
@ -23,14 +24,15 @@ public unsafe class MetaBaseFile : IDisposable
|
|||
protected void AllocateData( int length )
|
||||
{
|
||||
Length = length;
|
||||
Data = ( byte* )Marshal.AllocHGlobal( length );
|
||||
Data = ( byte* )MemoryHelper.GameAllocateDefault( ( ulong )length ); ;
|
||||
GC.AddMemoryPressure( length );
|
||||
}
|
||||
|
||||
// Free memory.
|
||||
protected void ReleaseUnmanagedResources()
|
||||
{
|
||||
Marshal.FreeHGlobal( ( IntPtr )Data );
|
||||
var ptr = ( IntPtr )Data;
|
||||
MemoryHelper.GameFree( ref ptr, (ulong) Length );
|
||||
GC.RemoveMemoryPressure( Length );
|
||||
Length = 0;
|
||||
Data = null;
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
using System;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Util;
|
||||
|
||||
namespace Penumbra.Meta.Files
|
||||
{
|
||||
// Contains all filenames for meta changes depending on their parameters.
|
||||
public static class MetaFileNames
|
||||
{
|
||||
public static GamePath Eqp()
|
||||
=> GamePath.GenerateUnchecked( "chara/xls/equipmentparameter/equipmentparameter.eqp" );
|
||||
|
||||
public static GamePath Gmp()
|
||||
=> GamePath.GenerateUnchecked( "chara/xls/equipmentparameter/gimmickparameter.gmp" );
|
||||
|
||||
public static GamePath Est( ObjectType type, EquipSlot equip, BodySlot slot )
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
ObjectType.Equipment => equip switch
|
||||
{
|
||||
EquipSlot.Body => GamePath.GenerateUnchecked( "chara/xls/charadb/extra_top.est" ),
|
||||
EquipSlot.Head => GamePath.GenerateUnchecked( "chara/xls/charadb/extra_met.est" ),
|
||||
_ => throw new NotImplementedException(),
|
||||
},
|
||||
ObjectType.Character => slot switch
|
||||
{
|
||||
BodySlot.Hair => GamePath.GenerateUnchecked( "chara/xls/charadb/hairskeletontemplate.est" ),
|
||||
BodySlot.Face => GamePath.GenerateUnchecked( "chara/xls/charadb/faceskeletontemplate.est" ),
|
||||
_ => throw new NotImplementedException(),
|
||||
},
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
|
||||
public static GamePath Imc( ObjectType type, ushort primaryId, ushort secondaryId )
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
ObjectType.Accessory => GamePath.GenerateUnchecked( $"chara/accessory/a{primaryId:D4}/a{primaryId:D4}.imc" ),
|
||||
ObjectType.Equipment => GamePath.GenerateUnchecked( $"chara/equipment/e{primaryId:D4}/e{primaryId:D4}.imc" ),
|
||||
ObjectType.DemiHuman => GamePath.GenerateUnchecked(
|
||||
$"chara/demihuman/d{primaryId:D4}/obj/equipment/e{secondaryId:D4}/e{secondaryId:D4}.imc" ),
|
||||
ObjectType.Monster => GamePath.GenerateUnchecked(
|
||||
$"chara/monster/m{primaryId:D4}/obj/body/b{secondaryId:D4}/b{secondaryId:D4}.imc" ),
|
||||
ObjectType.Weapon => GamePath.GenerateUnchecked(
|
||||
$"chara/weapon/w{primaryId:D4}/obj/body/b{secondaryId:D4}/b{secondaryId:D4}.imc" ),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
|
||||
public static GamePath Eqdp( ObjectType type, GenderRace gr )
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
ObjectType.Accessory => GamePath.GenerateUnchecked( $"chara/xls/charadb/accessorydeformerparameter/c{gr.ToRaceCode()}.eqdp" ),
|
||||
ObjectType.Equipment => GamePath.GenerateUnchecked( $"chara/xls/charadb/equipmentdeformerparameter/c{gr.ToRaceCode()}.eqdp" ),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
|
||||
public static GamePath Eqdp( EquipSlot slot, GenderRace gr )
|
||||
{
|
||||
return slot switch
|
||||
{
|
||||
EquipSlot.Head => Eqdp( ObjectType.Equipment, gr ),
|
||||
EquipSlot.Body => Eqdp( ObjectType.Equipment, gr ),
|
||||
EquipSlot.Feet => Eqdp( ObjectType.Equipment, gr ),
|
||||
EquipSlot.Hands => Eqdp( ObjectType.Equipment, gr ),
|
||||
EquipSlot.Legs => Eqdp( ObjectType.Equipment, gr ),
|
||||
EquipSlot.Neck => Eqdp( ObjectType.Accessory, gr ),
|
||||
EquipSlot.Ears => Eqdp( ObjectType.Accessory, gr ),
|
||||
EquipSlot.Wrists => Eqdp( ObjectType.Accessory, gr ),
|
||||
EquipSlot.LFinger => Eqdp( ObjectType.Accessory, gr ),
|
||||
EquipSlot.RFinger => Eqdp( ObjectType.Accessory, gr ),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
|
||||
public static GamePath Cmp()
|
||||
=> GamePath.GenerateUnchecked( "chara/xls/charamake/human.cmp" );
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Meta.Files;
|
||||
using ImcFile = Lumina.Data.Files.ImcFile;
|
||||
|
||||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
|
|
@ -58,4 +58,33 @@ public readonly struct ImcManipulation : IEquatable< ImcManipulation >
|
|||
|
||||
public override int GetHashCode()
|
||||
=> HashCode.Combine( PrimaryId, Variant, SecondaryId, ( int )ObjectType, ( int )EquipSlot, ( int )BodySlot );
|
||||
|
||||
public Utf8GamePath GamePath()
|
||||
{
|
||||
return ObjectType switch
|
||||
{
|
||||
ObjectType.Accessory => Utf8GamePath.FromString( $"chara/accessory/a{PrimaryId:D4}/a{PrimaryId:D4}.imc", out var p )
|
||||
? p
|
||||
: Utf8GamePath.Empty,
|
||||
ObjectType.Equipment => Utf8GamePath.FromString( $"chara/equipment/e{PrimaryId:D4}/e{PrimaryId:D4}.imc", out var p )
|
||||
? p
|
||||
: Utf8GamePath.Empty,
|
||||
ObjectType.DemiHuman => Utf8GamePath.FromString(
|
||||
$"chara/demihuman/d{PrimaryId:D4}/obj/equipment/e{SecondaryId:D4}/e{SecondaryId:D4}.imc", out var p )
|
||||
? p
|
||||
: Utf8GamePath.Empty,
|
||||
ObjectType.Monster => Utf8GamePath.FromString( $"chara/monster/m{PrimaryId:D4}/obj/body/b{SecondaryId:D4}/b{SecondaryId:D4}.imc",
|
||||
out var p )
|
||||
? p
|
||||
: Utf8GamePath.Empty,
|
||||
ObjectType.Weapon => Utf8GamePath.FromString( $"chara/weapon/w{PrimaryId:D4}/obj/body/b{SecondaryId:D4}/b{SecondaryId:D4}.imc",
|
||||
out var p )
|
||||
? p
|
||||
: Utf8GamePath.Empty,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
|
||||
public bool Apply( ImcFile file )
|
||||
=> file.SetEntry( ImcFile.PartIndex( EquipSlot ), Variant, Entry );
|
||||
}
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Penumbra.GameData.Util;
|
||||
|
||||
namespace Penumbra.Meta.Manipulations;
|
||||
|
||||
[StructLayout( LayoutKind.Explicit, Pack = 1, Size = 16 )]
|
||||
public readonly struct MetaManipulation : IEquatable< MetaManipulation >
|
||||
public readonly struct MetaManipulation : IEquatable< MetaManipulation >, IComparable<MetaManipulation>
|
||||
{
|
||||
public enum Type : byte
|
||||
{
|
||||
|
|
@ -106,4 +107,10 @@ public readonly struct MetaManipulation : IEquatable< MetaManipulation >
|
|||
Type.Imc => Imc.GetHashCode(),
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
|
||||
public unsafe int CompareTo( MetaManipulation other )
|
||||
{
|
||||
fixed(MetaManipulation* lhs = &this)
|
||||
return Functions.MemCmpUnchecked(lhs, &other, sizeof(MetaManipulation));
|
||||
}
|
||||
}
|
||||
|
|
@ -93,14 +93,14 @@ public class MetaCollection
|
|||
return false;
|
||||
}
|
||||
|
||||
if( option.Value.Any( manip => defaultFiles.CheckAgainstDefault( manip ) ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//if( option.Value.Any( manip => defaultFiles.CheckAgainstDefault( manip ) ) )
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
}
|
||||
}
|
||||
} // TODO
|
||||
|
||||
return DefaultData.All( manip => !defaultFiles.CheckAgainstDefault( manip ) );
|
||||
return true; //DefaultData.All( manip => !defaultFiles.CheckAgainstDefault( manip ) );
|
||||
}
|
||||
|
||||
// Re-sort all manipulations.
|
||||
|
|
@ -117,31 +117,33 @@ public class MetaCollection
|
|||
// Creates the option group and the option if necessary.
|
||||
private void AddMeta( string group, string option, TexToolsMeta meta )
|
||||
{
|
||||
if( meta.Manipulations.Count == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
var manipulations = meta.EqpManipulations.Select( m => new MetaManipulation( m ) )
|
||||
.Concat( meta.EqdpManipulations.Select( m => new MetaManipulation( m ) ) )
|
||||
.Concat( meta.EstManipulations.Select( m => new MetaManipulation( m ) ) )
|
||||
.Concat( meta.GmpManipulations.Select( m => new MetaManipulation( m ) ) )
|
||||
.Concat( meta.RspManipulations.Select( m => new MetaManipulation( m ) ) )
|
||||
.Concat( meta.ImcManipulations.Select( m => new MetaManipulation( m ) ) ).ToList();
|
||||
|
||||
if( group.Length == 0 )
|
||||
{
|
||||
DefaultData.AddRange( meta.Manipulations );
|
||||
DefaultData.AddRange( manipulations );
|
||||
}
|
||||
else if( option.Length == 0 )
|
||||
{ }
|
||||
else if( !GroupData.TryGetValue( group, out var options ) )
|
||||
{
|
||||
GroupData.Add( group, new Dictionary< string, List< MetaManipulation > >() { { option, meta.Manipulations.ToList() } } );
|
||||
GroupData.Add( group, new Dictionary< string, List< MetaManipulation > >() { { option, manipulations } } );
|
||||
}
|
||||
else if( !options.TryGetValue( option, out var list ) )
|
||||
{
|
||||
options.Add( option, meta.Manipulations.ToList() );
|
||||
options.Add( option, manipulations );
|
||||
}
|
||||
else
|
||||
{
|
||||
list.AddRange( meta.Manipulations );
|
||||
list.AddRange( manipulations );
|
||||
}
|
||||
|
||||
Count += meta.Manipulations.Count;
|
||||
Count += manipulations.Count;
|
||||
}
|
||||
|
||||
// Update the whole meta collection by reading all TexTools .meta files in a mod directory anew,
|
||||
|
|
@ -160,7 +162,7 @@ public class MetaCollection
|
|||
_ => TexToolsMeta.Invalid,
|
||||
};
|
||||
|
||||
if( metaData.FilePath == string.Empty || metaData.Manipulations.Count == 0 )
|
||||
if( metaData.FilePath == string.Empty )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,26 +2,19 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dalamud.Logging;
|
||||
using Lumina.Data.Files;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Interop;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Util;
|
||||
using Penumbra.Mods;
|
||||
using CharacterUtility = Penumbra.Interop.Structs.CharacterUtility;
|
||||
using ImcFile = Penumbra.Meta.Files.ImcFile;
|
||||
|
||||
namespace Penumbra.Meta;
|
||||
|
||||
public struct TemporaryImcFile : IDisposable
|
||||
{
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class MetaManager2 : IDisposable
|
||||
{
|
||||
public readonly List< MetaBaseFile > ChangedData = new(7 + CharacterUtility.NumEqdpFiles);
|
||||
|
|
@ -42,7 +35,51 @@ public class MetaManager2 : IDisposable
|
|||
public readonly Dictionary< EqdpManipulation, Mod.Mod > EqdpManipulations = new();
|
||||
|
||||
public readonly Dictionary< ImcManipulation, Mod.Mod > ImcManipulations = new();
|
||||
public readonly List< TemporaryImcFile > ImcFiles = new();
|
||||
public readonly Dictionary< Utf8GamePath, ImcFile > ImcFiles = new();
|
||||
|
||||
private readonly ModCollection _collection;
|
||||
|
||||
public unsafe void SetFiles()
|
||||
{
|
||||
foreach( var file in ChangedData )
|
||||
{
|
||||
Penumbra.CharacterUtility.SetResource( file.Index, ( IntPtr )file.Data, file.Length );
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue( MetaManipulation manip, out Mod.Mod? mod )
|
||||
{
|
||||
mod = manip.ManipulationType switch
|
||||
{
|
||||
MetaManipulation.Type.Eqp => EqpManipulations.TryGetValue( manip.Eqp, out var m ) ? m : null,
|
||||
MetaManipulation.Type.Gmp => GmpManipulations.TryGetValue( manip.Gmp, out var m ) ? m : null,
|
||||
MetaManipulation.Type.Eqdp => EqdpManipulations.TryGetValue( manip.Eqdp, out var m ) ? m : null,
|
||||
MetaManipulation.Type.Est => EstManipulations.TryGetValue( manip.Est, out var m ) ? m : null,
|
||||
MetaManipulation.Type.Rsp => RspManipulations.TryGetValue( manip.Rsp, out var m ) ? m : null,
|
||||
MetaManipulation.Type.Imc => ImcManipulations.TryGetValue( manip.Imc, out var m ) ? m : null,
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
return mod != null;
|
||||
}
|
||||
|
||||
public int Count
|
||||
=> ImcManipulations.Count
|
||||
+ EqdpManipulations.Count
|
||||
+ RspManipulations.Count
|
||||
+ GmpManipulations.Count
|
||||
+ EstManipulations.Count
|
||||
+ EqpManipulations.Count;
|
||||
|
||||
public MetaManager2( ModCollection collection )
|
||||
=> _collection = collection;
|
||||
|
||||
public void ApplyImcFiles( Dictionary< Utf8GamePath, FullPath > resolvedFiles )
|
||||
{
|
||||
foreach( var path in ImcFiles.Keys )
|
||||
{
|
||||
resolvedFiles[ path ] = CreateImcPath( path );
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetEqp()
|
||||
{
|
||||
|
|
@ -95,10 +132,21 @@ public class MetaManager2 : IDisposable
|
|||
EqdpManipulations.Clear();
|
||||
}
|
||||
|
||||
private FullPath CreateImcPath( Utf8GamePath path )
|
||||
{
|
||||
var d = new DirectoryInfo( $":{_collection.Name}/" );
|
||||
return new FullPath( d, new Utf8RelPath( path ) );
|
||||
}
|
||||
|
||||
public void ResetImc()
|
||||
{
|
||||
foreach( var file in ImcFiles )
|
||||
foreach( var (path, file) in ImcFiles )
|
||||
{
|
||||
_collection.Cache?.ResolvedFiles.Remove( path );
|
||||
path.Dispose();
|
||||
file.Dispose();
|
||||
}
|
||||
|
||||
ImcFiles.Clear();
|
||||
ImcManipulations.Clear();
|
||||
}
|
||||
|
|
@ -154,7 +202,6 @@ public class MetaManager2 : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public bool ApplyMod( EqpManipulation m, Mod.Mod mod )
|
||||
{
|
||||
if( !EqpManipulations.TryAdd( m, mod ) )
|
||||
|
|
@ -247,16 +294,63 @@ public class MetaManager2 : IDisposable
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool ApplyMod( ImcManipulation m, Mod.Mod mod )
|
||||
public unsafe bool ApplyMod( ImcManipulation m, Mod.Mod mod )
|
||||
{
|
||||
const uint imcExt = 0x00696D63;
|
||||
|
||||
if( !ImcManipulations.TryAdd( m, mod ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var path = m.GamePath();
|
||||
if( !ImcFiles.TryGetValue( path, out var file ) )
|
||||
{
|
||||
file = new ImcFile( path );
|
||||
}
|
||||
|
||||
if( !m.Apply( file ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ImcFiles[ path ] = file;
|
||||
var fullPath = CreateImcPath( path );
|
||||
if( _collection.Cache != null )
|
||||
{
|
||||
_collection.Cache.ResolvedFiles[ path ] = fullPath;
|
||||
}
|
||||
|
||||
var resource = ResourceLoader.FindResource( ResourceCategory.Chara, imcExt, ( uint )path.Path.Crc32 );
|
||||
if( resource != null )
|
||||
{
|
||||
file.Replace( ( ResourceHandle* )resource );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static unsafe byte ImcHandler( Utf8GamePath gamePath, ResourceManager* resourceManager,
|
||||
SeFileDescriptor* fileDescriptor, int priority, bool isSync )
|
||||
{
|
||||
var split = gamePath.Path.Split( ( byte )'|', 2, true );
|
||||
fileDescriptor->ResourceHandle->FileNameData = split[ 1 ].Path;
|
||||
fileDescriptor->ResourceHandle->FileNameLength = split[ 1 ].Length;
|
||||
|
||||
var ret = Penumbra.ResourceLoader.ReadSqPackHook.Original( resourceManager, fileDescriptor, priority, isSync );
|
||||
if( Penumbra.ModManager.Collections.Collections.TryGetValue( split[ 0 ].ToString(), out var collection )
|
||||
&& collection.Cache != null
|
||||
&& collection.Cache.MetaManipulations.ImcFiles.TryGetValue(
|
||||
Utf8GamePath.FromSpan( split[ 1 ].Span, out var p, false ) ? p : Utf8GamePath.Empty, out var file ) )
|
||||
{
|
||||
file.Replace( fileDescriptor->ResourceHandle );
|
||||
}
|
||||
|
||||
fileDescriptor->ResourceHandle->FileNameData = gamePath.Path.Path;
|
||||
fileDescriptor->ResourceHandle->FileNameLength = gamePath.Path.Length;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool ApplyMod( MetaManipulation m, Mod.Mod mod )
|
||||
{
|
||||
return m.ManipulationType switch
|
||||
|
|
@ -267,133 +361,7 @@ public class MetaManager2 : IDisposable
|
|||
MetaManipulation.Type.Est => ApplyMod( m.Est, mod ),
|
||||
MetaManipulation.Type.Rsp => ApplyMod( m.Rsp, mod ),
|
||||
MetaManipulation.Type.Imc => ApplyMod( m.Imc, mod ),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class MetaManager : IDisposable
|
||||
{
|
||||
internal class FileInformation
|
||||
{
|
||||
public readonly object Data;
|
||||
public bool Changed;
|
||||
public FullPath? CurrentFile;
|
||||
public byte[] ByteData = Array.Empty< byte >();
|
||||
|
||||
public FileInformation( object data )
|
||||
=> Data = data;
|
||||
|
||||
public void Write( DirectoryInfo dir, Utf8GamePath originalPath )
|
||||
{
|
||||
ByteData = Data switch
|
||||
{
|
||||
ImcFile imc => imc.WriteBytes(),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
DisposeFile( CurrentFile );
|
||||
CurrentFile = new FullPath( TempFile.WriteNew( dir, ByteData, $"_{originalPath.Filename()}" ) );
|
||||
Changed = false;
|
||||
}
|
||||
}
|
||||
|
||||
public const string TmpDirectory = "penumbrametatmp";
|
||||
|
||||
private readonly DirectoryInfo _dir;
|
||||
private readonly Dictionary< Utf8GamePath, FullPath > _resolvedFiles;
|
||||
|
||||
private readonly Dictionary< MetaManipulation, Mod.Mod > _currentManipulations = new();
|
||||
private readonly Dictionary< Utf8GamePath, FileInformation > _currentFiles = new();
|
||||
|
||||
public IEnumerable< (MetaManipulation, Mod.Mod) > Manipulations
|
||||
=> _currentManipulations.Select( kvp => ( kvp.Key, kvp.Value ) );
|
||||
|
||||
public IEnumerable< (Utf8GamePath, FullPath) > Files
|
||||
=> _currentFiles.Where( kvp => kvp.Value.CurrentFile != null )
|
||||
.Select( kvp => ( kvp.Key, kvp.Value.CurrentFile!.Value ) );
|
||||
|
||||
public int Count
|
||||
=> _currentManipulations.Count;
|
||||
|
||||
public bool TryGetValue( MetaManipulation manip, out Mod.Mod mod )
|
||||
=> _currentManipulations.TryGetValue( manip, out mod! );
|
||||
|
||||
public byte[] EqpData = Array.Empty< byte >();
|
||||
|
||||
private static void DisposeFile( FullPath? file )
|
||||
{
|
||||
if( !( file?.Exists ?? false ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.Delete( file.Value.FullName );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not delete temporary file \"{file.Value.FullName}\":\n{e}" );
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset( bool reload = true )
|
||||
{
|
||||
foreach( var file in _currentFiles )
|
||||
{
|
||||
_resolvedFiles.Remove( file.Key );
|
||||
DisposeFile( file.Value.CurrentFile );
|
||||
}
|
||||
|
||||
_currentManipulations.Clear();
|
||||
_currentFiles.Clear();
|
||||
ClearDirectory();
|
||||
if( reload )
|
||||
{
|
||||
Penumbra.ResidentResources.Reload();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
=> Reset();
|
||||
|
||||
private static void ClearDirectory( DirectoryInfo modDir )
|
||||
{
|
||||
modDir.Refresh();
|
||||
if( modDir.Exists )
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete( modDir.FullName, true );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not clear temporary metafile directory \"{modDir.FullName}\":\n{e}" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearDirectory()
|
||||
=> ClearDirectory( _dir );
|
||||
|
||||
public MetaManager( string name, Dictionary< Utf8GamePath, FullPath > resolvedFiles, DirectoryInfo tempDir )
|
||||
{
|
||||
_resolvedFiles = resolvedFiles;
|
||||
_dir = new DirectoryInfo( Path.Combine( tempDir.FullName, name.ReplaceBadXivSymbols() ) );
|
||||
ClearDirectory();
|
||||
}
|
||||
|
||||
public void WriteNewFiles()
|
||||
{
|
||||
if( _currentFiles.Any() )
|
||||
{
|
||||
Directory.CreateDirectory( _dir.FullName );
|
||||
}
|
||||
|
||||
foreach( var (key, value) in _currentFiles.Where( kvp => kvp.Value.Changed ) )
|
||||
{
|
||||
value.Write( _dir, key );
|
||||
_resolvedFiles[ key ] = value.CurrentFile!.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -67,15 +67,9 @@ public class CollectionManager
|
|||
|
||||
public void RecreateCaches()
|
||||
{
|
||||
if( !_manager.TempWritable )
|
||||
{
|
||||
PluginLog.Error( "No temporary directory available." );
|
||||
return;
|
||||
}
|
||||
|
||||
foreach( var collection in Collections.Values.Where( c => c.Cache != null ) )
|
||||
{
|
||||
collection.CreateCache( _manager.TempPath, _manager.StructuredMods.AllMods( _manager.Config.SortFoldersFirst ) );
|
||||
collection.CreateCache( _manager.StructuredMods.AllMods( _manager.Config.SortFoldersFirst ) );
|
||||
}
|
||||
|
||||
CreateNecessaryCaches();
|
||||
|
|
@ -176,15 +170,9 @@ public class CollectionManager
|
|||
|
||||
private void AddCache( ModCollection collection )
|
||||
{
|
||||
if( !_manager.TempWritable )
|
||||
{
|
||||
PluginLog.Error( "No tmp directory available." );
|
||||
return;
|
||||
}
|
||||
|
||||
if( collection.Cache == null && collection.Name != string.Empty )
|
||||
{
|
||||
collection.CreateCache( _manager.TempPath, _manager.StructuredMods.AllMods( _manager.Config.SortFoldersFirst ) );
|
||||
collection.CreateCache( _manager.StructuredMods.AllMods( _manager.Config.SortFoldersFirst ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -289,7 +277,7 @@ public class CollectionManager
|
|||
CurrentCollection = Collections[ ModCollection.DefaultCollection ];
|
||||
if( CurrentCollection.Cache == null )
|
||||
{
|
||||
CurrentCollection.CreateCache( _manager.TempPath, _manager.StructuredMods.AllMods( _manager.Config.SortFoldersFirst ) );
|
||||
CurrentCollection.CreateCache( _manager.StructuredMods.AllMods( _manager.Config.SortFoldersFirst ) );
|
||||
}
|
||||
|
||||
config.CurrentCollection = ModCollection.DefaultCollection;
|
||||
|
|
|
|||
|
|
@ -65,9 +65,9 @@ public class ModCollection
|
|||
return removeList.Length > 0;
|
||||
}
|
||||
|
||||
public void CreateCache( DirectoryInfo modDirectory, IEnumerable< ModData > data )
|
||||
public void CreateCache( IEnumerable< ModData > data )
|
||||
{
|
||||
Cache = new ModCollectionCache( Name, modDirectory );
|
||||
Cache = new ModCollectionCache( this );
|
||||
var changedSettings = false;
|
||||
foreach( var mod in data )
|
||||
{
|
||||
|
|
@ -89,7 +89,7 @@ public class ModCollection
|
|||
Save();
|
||||
}
|
||||
|
||||
CalculateEffectiveFileList( modDirectory, true, false );
|
||||
CalculateEffectiveFileList( true, false );
|
||||
}
|
||||
|
||||
public void ClearCache()
|
||||
|
|
@ -135,11 +135,11 @@ public class ModCollection
|
|||
}
|
||||
}
|
||||
|
||||
public void CalculateEffectiveFileList( DirectoryInfo modDir, bool withMetaManipulations, bool activeCollection )
|
||||
public void CalculateEffectiveFileList( bool withMetaManipulations, bool activeCollection )
|
||||
{
|
||||
PluginLog.Debug( "Recalculating effective file list for {CollectionName} [{WithMetaManipulations}] [{IsActiveCollection}]", Name,
|
||||
withMetaManipulations, activeCollection );
|
||||
Cache ??= new ModCollectionCache( Name, modDir );
|
||||
Cache ??= new ModCollectionCache( this );
|
||||
UpdateSettings( false );
|
||||
Cache.CalculateEffectiveFileList();
|
||||
if( withMetaManipulations )
|
||||
|
|
|
|||
|
|
@ -27,8 +27,7 @@ public class ModCollectionCache
|
|||
private readonly SortedList< string, object? > _changedItems = new();
|
||||
public readonly Dictionary< Utf8GamePath, FullPath > ResolvedFiles = new();
|
||||
public readonly HashSet< FullPath > MissingFiles = new();
|
||||
public readonly HashSet< ulong > Checksums = new();
|
||||
public readonly MetaManager MetaManipulations;
|
||||
public readonly MetaManager2 MetaManipulations;
|
||||
|
||||
public IReadOnlyDictionary< string, object? > ChangedItems
|
||||
{
|
||||
|
|
@ -39,8 +38,8 @@ public class ModCollectionCache
|
|||
}
|
||||
}
|
||||
|
||||
public ModCollectionCache( string collectionName, DirectoryInfo tempDir )
|
||||
=> MetaManipulations = new MetaManager( collectionName, ResolvedFiles, tempDir );
|
||||
public ModCollectionCache( ModCollection collection )
|
||||
=> MetaManipulations = new MetaManager2( collection );
|
||||
|
||||
private static void ResetFileSeen( int size )
|
||||
{
|
||||
|
|
@ -73,11 +72,6 @@ public class ModCollectionCache
|
|||
}
|
||||
|
||||
AddMetaFiles();
|
||||
Checksums.Clear();
|
||||
foreach( var file in ResolvedFiles )
|
||||
{
|
||||
Checksums.Add( file.Value.Crc64 );
|
||||
}
|
||||
}
|
||||
|
||||
private void SetChangedItems()
|
||||
|
|
@ -89,11 +83,10 @@ public class ModCollectionCache
|
|||
|
||||
try
|
||||
{
|
||||
// Skip meta files because IMCs would result in far too many false-positive items,
|
||||
// Skip IMCs because they would result in far too many false-positive items,
|
||||
// since they are per set instead of per item-slot/item/variant.
|
||||
var metaFiles = MetaManipulations.Files.Select( p => p.Item1 ).ToHashSet();
|
||||
var identifier = GameData.GameData.GetIdentifier();
|
||||
foreach( var resolved in ResolvedFiles.Keys.Where( file => !metaFiles.Contains( file ) ) )
|
||||
foreach( var resolved in ResolvedFiles.Keys.Where( file => !file.Path.EndsWith( 'i', 'm', 'c' ) ) )
|
||||
{
|
||||
identifier.Identify( _changedItems, resolved.ToGamePath() );
|
||||
}
|
||||
|
|
@ -265,18 +258,7 @@ public class ModCollectionCache
|
|||
}
|
||||
|
||||
private void AddMetaFiles()
|
||||
{
|
||||
foreach( var (gamePath, file) in MetaManipulations.Files )
|
||||
{
|
||||
if( RegisteredFiles.TryGetValue( gamePath, out var mod ) )
|
||||
{
|
||||
PluginLog.Warning(
|
||||
$"The meta manipulation file {gamePath} was already completely replaced by {mod.Data.Meta.Name}. This is probably a mistake. Using the custom file {file.FullName}." );
|
||||
}
|
||||
|
||||
ResolvedFiles[ gamePath ] = file;
|
||||
}
|
||||
}
|
||||
=> MetaManipulations.ApplyImcFiles( ResolvedFiles );
|
||||
|
||||
private void AddSwaps( Mod.Mod mod )
|
||||
{
|
||||
|
|
@ -304,12 +286,12 @@ public class ModCollectionCache
|
|||
{
|
||||
if( !MetaManipulations.TryGetValue( manip, out var oldMod ) )
|
||||
{
|
||||
//MetaManipulations.ApplyMod( manip, mod );
|
||||
MetaManipulations.ApplyMod( manip, mod );
|
||||
}
|
||||
else
|
||||
{
|
||||
mod.Cache.AddConflict( oldMod, manip );
|
||||
if( !ReferenceEquals( mod, oldMod ) && mod.Settings.Priority == oldMod.Settings.Priority )
|
||||
mod.Cache.AddConflict( oldMod!, manip );
|
||||
if( !ReferenceEquals( mod, oldMod ) && mod.Settings.Priority == oldMod!.Settings.Priority )
|
||||
{
|
||||
oldMod.Cache.AddConflict( mod, manip );
|
||||
}
|
||||
|
|
@ -319,15 +301,13 @@ public class ModCollectionCache
|
|||
|
||||
public void UpdateMetaManipulations()
|
||||
{
|
||||
MetaManipulations.Reset( false );
|
||||
MetaManipulations.Reset();
|
||||
|
||||
foreach( var mod in AvailableMods.Values.Where( m => m.Settings.Enabled && m.Data.Resources.MetaManipulations.Count > 0 ) )
|
||||
{
|
||||
mod.Cache.ClearMetaConflicts();
|
||||
AddManipulations( mod );
|
||||
}
|
||||
|
||||
MetaManipulations.WriteNewFiles();
|
||||
}
|
||||
|
||||
public void RemoveMod( DirectoryInfo basePath )
|
||||
|
|
|
|||
110
Penumbra/Mods/ModManager.Directory.cs
Normal file
110
Penumbra/Mods/ModManager.Directory.cs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Dalamud.Logging;
|
||||
using Penumbra.Mod;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
public partial class ModManagerNew
|
||||
{
|
||||
private readonly List<ModData> _mods = new();
|
||||
public IReadOnlyList<ModData> Mods
|
||||
=> _mods;
|
||||
|
||||
public void DiscoverMods()
|
||||
{
|
||||
//_mods.Clear();
|
||||
//
|
||||
//if( CheckValidity() )
|
||||
//{
|
||||
// foreach( var modFolder in BasePath.EnumerateDirectories() )
|
||||
// {
|
||||
// var mod = ModData.LoadMod( StructuredMods, modFolder );
|
||||
// if( mod == null )
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// Mods.Add( modFolder.Name, mod );
|
||||
// }
|
||||
//
|
||||
// SetModStructure();
|
||||
//}
|
||||
//
|
||||
//Collections.RecreateCaches();
|
||||
}
|
||||
}
|
||||
|
||||
public partial class ModManagerNew
|
||||
{
|
||||
public DirectoryInfo BasePath { get; private set; } = null!;
|
||||
public bool Valid { get; private set; }
|
||||
|
||||
public event Action< DirectoryInfo >? BasePathChanged;
|
||||
|
||||
public ModManagerNew()
|
||||
{
|
||||
InitBaseDirectory( Penumbra.Config.ModDirectory );
|
||||
}
|
||||
|
||||
public bool CheckValidity()
|
||||
{
|
||||
if( Valid )
|
||||
{
|
||||
Valid = Directory.Exists(BasePath.FullName);
|
||||
}
|
||||
|
||||
return Valid;
|
||||
}
|
||||
|
||||
private static (DirectoryInfo, bool) CreateDirectory( string path )
|
||||
{
|
||||
var newDir = new DirectoryInfo( path );
|
||||
if( !newDir.Exists )
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory( newDir.FullName );
|
||||
newDir.Refresh();
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not create specified mod directory {newDir.FullName}:\n{e}" );
|
||||
return ( newDir, false );
|
||||
}
|
||||
}
|
||||
|
||||
return ( newDir, true );
|
||||
}
|
||||
|
||||
private void InitBaseDirectory( string path )
|
||||
{
|
||||
if( path.Length == 0 )
|
||||
{
|
||||
Valid = false;
|
||||
BasePath = new DirectoryInfo( "." );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
( BasePath, Valid ) = CreateDirectory( path );
|
||||
|
||||
if( Penumbra.Config.ModDirectory != BasePath.FullName )
|
||||
{
|
||||
Penumbra.Config.ModDirectory = BasePath.FullName;
|
||||
Penumbra.Config.Save();
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeBaseDirectory( string path )
|
||||
{
|
||||
if( string.Equals( path, Penumbra.Config.ModDirectory, StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InitBaseDirectory( path );
|
||||
BasePathChanged?.Invoke( BasePath );
|
||||
}
|
||||
}
|
||||
|
|
@ -4,401 +4,297 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using Dalamud.Logging;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.GameData.Util;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Mod;
|
||||
|
||||
namespace Penumbra.Mods
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
// The ModManager handles the basic mods installed to the mod directory.
|
||||
// It also contains the CollectionManager that handles all collections.
|
||||
public class ModManager
|
||||
{
|
||||
// The ModManager handles the basic mods installed to the mod directory.
|
||||
// It also contains the CollectionManager that handles all collections.
|
||||
public class ModManager
|
||||
public DirectoryInfo BasePath { get; private set; } = null!;
|
||||
|
||||
public Dictionary< string, ModData > Mods { get; } = new();
|
||||
public ModFolder StructuredMods { get; } = ModFileSystem.Root;
|
||||
|
||||
public CollectionManager Collections { get; }
|
||||
|
||||
public bool Valid { get; private set; }
|
||||
|
||||
public Configuration Config
|
||||
=> Penumbra.Config;
|
||||
|
||||
public void DiscoverMods( string newDir )
|
||||
{
|
||||
public DirectoryInfo BasePath { get; private set; } = null!;
|
||||
public DirectoryInfo TempPath { get; private set; } = null!;
|
||||
SetBaseDirectory( newDir, false );
|
||||
DiscoverMods();
|
||||
}
|
||||
|
||||
public Dictionary< string, ModData > Mods { get; } = new();
|
||||
public ModFolder StructuredMods { get; } = ModFileSystem.Root;
|
||||
|
||||
public CollectionManager Collections { get; }
|
||||
|
||||
public bool Valid { get; private set; }
|
||||
public bool TempWritable { get; private set; }
|
||||
|
||||
public Configuration Config
|
||||
=> Penumbra.Config;
|
||||
|
||||
public void DiscoverMods( string newDir )
|
||||
private void SetBaseDirectory( string newPath, bool firstTime )
|
||||
{
|
||||
if( !firstTime && string.Equals( newPath, Config.ModDirectory, StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
SetBaseDirectory( newDir, false );
|
||||
DiscoverMods();
|
||||
return;
|
||||
}
|
||||
|
||||
private void ClearOldTmpDir()
|
||||
if( !newPath.Any() )
|
||||
{
|
||||
if( !TempWritable )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TempPath.Refresh();
|
||||
if( TempPath.Exists )
|
||||
Valid = false;
|
||||
BasePath = new DirectoryInfo( "." );
|
||||
}
|
||||
else
|
||||
{
|
||||
var newDir = new DirectoryInfo( newPath );
|
||||
if( !newDir.Exists )
|
||||
{
|
||||
try
|
||||
{
|
||||
TempPath.Delete( true );
|
||||
Directory.CreateDirectory( newDir.FullName );
|
||||
newDir.Refresh();
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not delete temporary directory {TempPath.FullName}:\n{e}" );
|
||||
PluginLog.Error( $"Could not create specified mod directory {newDir.FullName}:\n{e}" );
|
||||
}
|
||||
}
|
||||
|
||||
BasePath = newDir;
|
||||
Valid = true;
|
||||
if( Config.ModDirectory != BasePath.FullName )
|
||||
{
|
||||
Config.ModDirectory = BasePath.FullName;
|
||||
Config.Save();
|
||||
}
|
||||
|
||||
if( !firstTime )
|
||||
{
|
||||
Collections.RecreateCaches();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ModManager()
|
||||
{
|
||||
SetBaseDirectory( Config.ModDirectory, true );
|
||||
Collections = new CollectionManager( this );
|
||||
}
|
||||
|
||||
private bool SetSortOrderPath( ModData mod, string path )
|
||||
{
|
||||
mod.Move( path );
|
||||
var fixedPath = mod.SortOrder.FullPath;
|
||||
if( !fixedPath.Any() || string.Equals( fixedPath, mod.Meta.Name, StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
Config.ModSortOrder.Remove( mod.BasePath.Name );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( path != fixedPath )
|
||||
{
|
||||
Config.ModSortOrder[ mod.BasePath.Name ] = fixedPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetModStructure( bool removeOldPaths = false )
|
||||
{
|
||||
var changes = false;
|
||||
|
||||
foreach( var kvp in Config.ModSortOrder.ToArray() )
|
||||
{
|
||||
if( kvp.Value.Any() && Mods.TryGetValue( kvp.Key, out var mod ) )
|
||||
{
|
||||
changes |= SetSortOrderPath( mod, kvp.Value );
|
||||
}
|
||||
else if( removeOldPaths )
|
||||
{
|
||||
changes = true;
|
||||
Config.ModSortOrder.Remove( kvp.Key );
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CheckTmpDir( string newPath, out DirectoryInfo tmpDir )
|
||||
if( changes )
|
||||
{
|
||||
tmpDir = new DirectoryInfo( Path.Combine( newPath, MetaManager.TmpDirectory ) );
|
||||
try
|
||||
Config.Save();
|
||||
}
|
||||
}
|
||||
|
||||
public void DiscoverMods()
|
||||
{
|
||||
Mods.Clear();
|
||||
BasePath.Refresh();
|
||||
|
||||
StructuredMods.SubFolders.Clear();
|
||||
StructuredMods.Mods.Clear();
|
||||
if( Valid && BasePath.Exists )
|
||||
{
|
||||
foreach( var modFolder in BasePath.EnumerateDirectories() )
|
||||
{
|
||||
if( tmpDir.Exists )
|
||||
var mod = ModData.LoadMod( StructuredMods, modFolder );
|
||||
if( mod == null )
|
||||
{
|
||||
tmpDir.Delete( true );
|
||||
tmpDir.Refresh();
|
||||
continue;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory( tmpDir.FullName );
|
||||
tmpDir.Refresh();
|
||||
return true;
|
||||
Mods.Add( modFolder.Name, mod );
|
||||
}
|
||||
|
||||
SetModStructure();
|
||||
}
|
||||
|
||||
Collections.RecreateCaches();
|
||||
}
|
||||
|
||||
public void DeleteMod( DirectoryInfo modFolder )
|
||||
{
|
||||
modFolder.Refresh();
|
||||
if( modFolder.Exists )
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete( modFolder.FullName, true );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not create temporary directory {tmpDir.FullName}:\n{e}" );
|
||||
return false;
|
||||
PluginLog.Error( $"Could not delete the mod {modFolder.Name}:\n{e}" );
|
||||
}
|
||||
|
||||
if( Mods.TryGetValue( modFolder.Name, out var mod ) )
|
||||
{
|
||||
mod.SortOrder.ParentFolder.RemoveMod( mod );
|
||||
Mods.Remove( modFolder.Name );
|
||||
Collections.RemoveModFromCaches( modFolder );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBaseDirectory( string newPath, bool firstTime )
|
||||
public bool AddMod( DirectoryInfo modFolder )
|
||||
{
|
||||
var mod = ModData.LoadMod( StructuredMods, modFolder );
|
||||
if( mod == null )
|
||||
{
|
||||
if( !firstTime && string.Equals( newPath, Config.ModDirectory, StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( !newPath.Any() )
|
||||
{
|
||||
Valid = false;
|
||||
BasePath = new DirectoryInfo( "." );
|
||||
}
|
||||
else
|
||||
{
|
||||
var newDir = new DirectoryInfo( newPath );
|
||||
if( !newDir.Exists )
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory( newDir.FullName );
|
||||
newDir.Refresh();
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not create specified mod directory {newDir.FullName}:\n{e}" );
|
||||
}
|
||||
}
|
||||
|
||||
BasePath = newDir;
|
||||
Valid = true;
|
||||
if( Config.ModDirectory != BasePath.FullName )
|
||||
{
|
||||
Config.ModDirectory = BasePath.FullName;
|
||||
Config.Save();
|
||||
}
|
||||
|
||||
if( !Config.TempDirectory.Any() )
|
||||
{
|
||||
if( CheckTmpDir( BasePath.FullName, out var newTmpDir ) )
|
||||
{
|
||||
if( !firstTime )
|
||||
{
|
||||
ClearOldTmpDir();
|
||||
}
|
||||
|
||||
TempPath = newTmpDir;
|
||||
TempWritable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
TempWritable = false;
|
||||
}
|
||||
}
|
||||
|
||||
if( !firstTime )
|
||||
{
|
||||
Collections.RecreateCaches();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetTempDirectory( string newPath, bool firstTime )
|
||||
{
|
||||
if( !Valid || !firstTime && string.Equals( newPath, Config.TempDirectory, StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( !newPath.Any() && CheckTmpDir( BasePath.FullName, out var newTmpDir )
|
||||
|| newPath.Any() && CheckTmpDir( newPath, out newTmpDir ) )
|
||||
{
|
||||
if( !firstTime )
|
||||
{
|
||||
ClearOldTmpDir();
|
||||
}
|
||||
|
||||
TempPath = newTmpDir;
|
||||
TempWritable = true;
|
||||
var newName = newPath.Any() ? TempPath.Parent!.FullName : string.Empty;
|
||||
if( Config.TempDirectory != newName )
|
||||
{
|
||||
Config.TempDirectory = newName;
|
||||
Config.Save();
|
||||
}
|
||||
|
||||
if( !firstTime )
|
||||
{
|
||||
Collections.RecreateCaches();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TempWritable = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTempDirectory( string newPath )
|
||||
=> SetTempDirectory( newPath, false );
|
||||
|
||||
public ModManager()
|
||||
{
|
||||
SetBaseDirectory( Config.ModDirectory, true );
|
||||
SetTempDirectory( Config.TempDirectory, true );
|
||||
Collections = new CollectionManager( this );
|
||||
}
|
||||
|
||||
private bool SetSortOrderPath( ModData mod, string path )
|
||||
{
|
||||
mod.Move( path );
|
||||
var fixedPath = mod.SortOrder.FullPath;
|
||||
if( !fixedPath.Any() || string.Equals( fixedPath, mod.Meta.Name, StringComparison.InvariantCultureIgnoreCase ) )
|
||||
{
|
||||
Config.ModSortOrder.Remove( mod.BasePath.Name );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( path != fixedPath )
|
||||
{
|
||||
Config.ModSortOrder[ mod.BasePath.Name ] = fixedPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetModStructure( bool removeOldPaths = false )
|
||||
if( Config.ModSortOrder.TryGetValue( mod.BasePath.Name, out var sortOrder ) )
|
||||
{
|
||||
var changes = false;
|
||||
|
||||
foreach( var kvp in Config.ModSortOrder.ToArray() )
|
||||
{
|
||||
if( kvp.Value.Any() && Mods.TryGetValue( kvp.Key, out var mod ) )
|
||||
{
|
||||
changes |= SetSortOrderPath( mod, kvp.Value );
|
||||
}
|
||||
else if( removeOldPaths )
|
||||
{
|
||||
changes = true;
|
||||
Config.ModSortOrder.Remove( kvp.Key );
|
||||
}
|
||||
}
|
||||
|
||||
if( changes )
|
||||
if( SetSortOrderPath( mod, sortOrder ) )
|
||||
{
|
||||
Config.Save();
|
||||
}
|
||||
}
|
||||
|
||||
public void DiscoverMods()
|
||||
if( Mods.ContainsKey( modFolder.Name ) )
|
||||
{
|
||||
Mods.Clear();
|
||||
BasePath.Refresh();
|
||||
|
||||
StructuredMods.SubFolders.Clear();
|
||||
StructuredMods.Mods.Clear();
|
||||
if( Valid && BasePath.Exists )
|
||||
{
|
||||
foreach( var modFolder in BasePath.EnumerateDirectories() )
|
||||
{
|
||||
var mod = ModData.LoadMod( StructuredMods, modFolder );
|
||||
if( mod == null )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Mods.Add( modFolder.Name, mod );
|
||||
}
|
||||
|
||||
SetModStructure();
|
||||
}
|
||||
|
||||
Collections.RecreateCaches();
|
||||
return false;
|
||||
}
|
||||
|
||||
public void DeleteMod( DirectoryInfo modFolder )
|
||||
Mods.Add( modFolder.Name, mod );
|
||||
foreach( var collection in Collections.Collections.Values )
|
||||
{
|
||||
modFolder.Refresh();
|
||||
if( modFolder.Exists )
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete( modFolder.FullName, true );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not delete the mod {modFolder.Name}:\n{e}" );
|
||||
}
|
||||
|
||||
if( Mods.TryGetValue( modFolder.Name, out var mod ) )
|
||||
{
|
||||
mod.SortOrder.ParentFolder.RemoveMod( mod );
|
||||
Mods.Remove( modFolder.Name );
|
||||
Collections.RemoveModFromCaches( modFolder );
|
||||
}
|
||||
}
|
||||
collection.AddMod( mod );
|
||||
}
|
||||
|
||||
public bool AddMod( DirectoryInfo modFolder )
|
||||
{
|
||||
var mod = ModData.LoadMod( StructuredMods, modFolder );
|
||||
if( mod == null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool UpdateMod( ModData mod, bool reloadMeta = false, bool recomputeMeta = false, bool force = false )
|
||||
{
|
||||
var oldName = mod.Meta.Name;
|
||||
var metaChanges = mod.Meta.RefreshFromFile( mod.MetaFile ) || force;
|
||||
var fileChanges = mod.Resources.RefreshModFiles( mod.BasePath );
|
||||
|
||||
if( !recomputeMeta && !reloadMeta && !metaChanges && fileChanges == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( metaChanges || fileChanges.HasFlag( ResourceChange.Files ) )
|
||||
{
|
||||
mod.ComputeChangedItems();
|
||||
if( Config.ModSortOrder.TryGetValue( mod.BasePath.Name, out var sortOrder ) )
|
||||
{
|
||||
if( SetSortOrderPath( mod, sortOrder ) )
|
||||
mod.Move( sortOrder );
|
||||
var path = mod.SortOrder.FullPath;
|
||||
if( path != sortOrder )
|
||||
{
|
||||
Config.ModSortOrder[ mod.BasePath.Name ] = path;
|
||||
Config.Save();
|
||||
}
|
||||
}
|
||||
|
||||
if( Mods.ContainsKey( modFolder.Name ) )
|
||||
else
|
||||
{
|
||||
return false;
|
||||
mod.SortOrder = new SortOrder( StructuredMods, mod.Meta.Name );
|
||||
}
|
||||
|
||||
Mods.Add( modFolder.Name, mod );
|
||||
foreach( var collection in Collections.Collections.Values )
|
||||
{
|
||||
collection.AddMod( mod );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool UpdateMod( ModData mod, bool reloadMeta = false, bool recomputeMeta = false, bool force = false )
|
||||
var nameChange = !string.Equals( oldName, mod.Meta.Name, StringComparison.InvariantCulture );
|
||||
|
||||
recomputeMeta |= fileChanges.HasFlag( ResourceChange.Meta );
|
||||
if( recomputeMeta )
|
||||
{
|
||||
var oldName = mod.Meta.Name;
|
||||
var metaChanges = mod.Meta.RefreshFromFile( mod.MetaFile ) || force;
|
||||
var fileChanges = mod.Resources.RefreshModFiles( mod.BasePath );
|
||||
|
||||
if( !recomputeMeta && !reloadMeta && !metaChanges && fileChanges == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( metaChanges || fileChanges.HasFlag( ResourceChange.Files ) )
|
||||
{
|
||||
mod.ComputeChangedItems();
|
||||
if( Config.ModSortOrder.TryGetValue( mod.BasePath.Name, out var sortOrder ) )
|
||||
{
|
||||
mod.Move( sortOrder );
|
||||
var path = mod.SortOrder.FullPath;
|
||||
if( path != sortOrder )
|
||||
{
|
||||
Config.ModSortOrder[ mod.BasePath.Name ] = path;
|
||||
Config.Save();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mod.SortOrder = new SortOrder( StructuredMods, mod.Meta.Name );
|
||||
}
|
||||
}
|
||||
|
||||
var nameChange = !string.Equals( oldName, mod.Meta.Name, StringComparison.InvariantCulture );
|
||||
|
||||
recomputeMeta |= fileChanges.HasFlag( ResourceChange.Meta );
|
||||
if( recomputeMeta )
|
||||
{
|
||||
mod.Resources.MetaManipulations.Update( mod.Resources.MetaFiles, mod.BasePath, mod.Meta );
|
||||
mod.Resources.MetaManipulations.SaveToFile( MetaCollection.FileName( mod.BasePath ) );
|
||||
}
|
||||
|
||||
Collections.UpdateCollections( mod, metaChanges, fileChanges, nameChange, reloadMeta );
|
||||
|
||||
return true;
|
||||
mod.Resources.MetaManipulations.Update( mod.Resources.MetaFiles, mod.BasePath, mod.Meta );
|
||||
mod.Resources.MetaManipulations.SaveToFile( MetaCollection.FileName( mod.BasePath ) );
|
||||
}
|
||||
|
||||
public FullPath? ResolveSwappedOrReplacementPath( Utf8GamePath gameResourcePath )
|
||||
{
|
||||
var ret = Collections.ActiveCollection.ResolveSwappedOrReplacementPath( gameResourcePath );
|
||||
ret ??= Collections.ForcedCollection.ResolveSwappedOrReplacementPath( gameResourcePath );
|
||||
return ret;
|
||||
}
|
||||
Collections.UpdateCollections( mod, metaChanges, fileChanges, nameChange, reloadMeta );
|
||||
|
||||
// private void FileSystemWatcherOnChanged( object sender, FileSystemEventArgs e )
|
||||
// {
|
||||
// #if DEBUG
|
||||
// PluginLog.Verbose( "file changed: {FullPath}", e.FullPath );
|
||||
// #endif
|
||||
//
|
||||
// if( _plugin.ImportInProgress )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if( _plugin.Configuration.DisableFileSystemNotifications )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// var file = e.FullPath;
|
||||
//
|
||||
// if( !ResolvedFiles.Any( x => x.Value.FullName == file ) )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// PluginLog.Log( "a loaded file has been modified - file: {FullPath}", file );
|
||||
// _plugin.GameUtils.ReloadPlayerResources();
|
||||
// }
|
||||
//
|
||||
// private void FileSystemPasta()
|
||||
// {
|
||||
// haha spaghet
|
||||
// _fileSystemWatcher?.Dispose();
|
||||
// _fileSystemWatcher = new FileSystemWatcher( _basePath.FullName )
|
||||
// {
|
||||
// NotifyFilter = NotifyFilters.LastWrite |
|
||||
// NotifyFilters.FileName |
|
||||
// NotifyFilters.DirectoryName,
|
||||
// IncludeSubdirectories = true,
|
||||
// EnableRaisingEvents = true
|
||||
// };
|
||||
//
|
||||
// _fileSystemWatcher.Changed += FileSystemWatcherOnChanged;
|
||||
// _fileSystemWatcher.Created += FileSystemWatcherOnChanged;
|
||||
// _fileSystemWatcher.Deleted += FileSystemWatcherOnChanged;
|
||||
// _fileSystemWatcher.Renamed += FileSystemWatcherOnChanged;
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
public FullPath? ResolveSwappedOrReplacementPath( Utf8GamePath gameResourcePath )
|
||||
{
|
||||
var ret = Collections.ActiveCollection.ResolveSwappedOrReplacementPath( gameResourcePath );
|
||||
ret ??= Collections.ForcedCollection.ResolveSwappedOrReplacementPath( gameResourcePath );
|
||||
return ret;
|
||||
}
|
||||
|
||||
// private void FileSystemWatcherOnChanged( object sender, FileSystemEventArgs e )
|
||||
// {
|
||||
// #if DEBUG
|
||||
// PluginLog.Verbose( "file changed: {FullPath}", e.FullPath );
|
||||
// #endif
|
||||
//
|
||||
// if( _plugin.ImportInProgress )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if( _plugin.Configuration.DisableFileSystemNotifications )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// var file = e.FullPath;
|
||||
//
|
||||
// if( !ResolvedFiles.Any( x => x.Value.FullName == file ) )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// PluginLog.Log( "a loaded file has been modified - file: {FullPath}", file );
|
||||
// _plugin.GameUtils.ReloadPlayerResources();
|
||||
// }
|
||||
//
|
||||
// private void FileSystemPasta()
|
||||
// {
|
||||
// haha spaghet
|
||||
// _fileSystemWatcher?.Dispose();
|
||||
// _fileSystemWatcher = new FileSystemWatcher( _basePath.FullName )
|
||||
// {
|
||||
// NotifyFilter = NotifyFilters.LastWrite |
|
||||
// NotifyFilters.FileName |
|
||||
// NotifyFilters.DirectoryName,
|
||||
// IncludeSubdirectories = true,
|
||||
// EnableRaisingEvents = true
|
||||
// };
|
||||
//
|
||||
// _fileSystemWatcher.Changed += FileSystemWatcherOnChanged;
|
||||
// _fileSystemWatcher.Created += FileSystemWatcherOnChanged;
|
||||
// _fileSystemWatcher.Deleted += FileSystemWatcherOnChanged;
|
||||
// _fileSystemWatcher.Renamed += FileSystemWatcherOnChanged;
|
||||
// }
|
||||
}
|
||||
|
|
@ -204,7 +204,7 @@ public static class ModManagerEditExtensions
|
|||
collection.Save();
|
||||
if( collection.Cache != null && settings.Enabled )
|
||||
{
|
||||
collection.CalculateEffectiveFileList( manager.TempPath, mod.Resources.MetaManipulations.Count > 0,
|
||||
collection.CalculateEffectiveFileList( mod.Resources.MetaManipulations.Count > 0,
|
||||
collection == manager.Collections.ActiveCollection );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ using System.Linq;
|
|||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra;
|
||||
public class MetaDefaults
|
||||
{
|
||||
|
||||
}
|
||||
public class MetaDefaults
|
||||
{ }
|
||||
|
||||
public class Penumbra : IDalamudPlugin
|
||||
{
|
||||
public string Name
|
||||
|
|
@ -41,8 +41,7 @@ public class Penumbra : IDalamudPlugin
|
|||
public static MetaDefaults MetaDefaults { get; private set; } = null!;
|
||||
public static ModManager ModManager { get; private set; } = null!;
|
||||
|
||||
|
||||
public ResourceLoader ResourceLoader { get; }
|
||||
public static ResourceLoader ResourceLoader { get; set; } = null!;
|
||||
public ResourceLogger ResourceLogger { get; }
|
||||
|
||||
//public PathResolver PathResolver { get; }
|
||||
|
|
@ -113,12 +112,20 @@ public class Penumbra : IDalamudPlugin
|
|||
};
|
||||
|
||||
ResourceLoader.EnableHooks();
|
||||
if (Config.EnableMods)
|
||||
if( Config.EnableMods )
|
||||
{
|
||||
ResourceLoader.EnableReplacements();
|
||||
if (Config.DebugMode)
|
||||
}
|
||||
|
||||
if( Config.DebugMode )
|
||||
{
|
||||
ResourceLoader.EnableDebug();
|
||||
if (Config.EnableFullResourceLogging)
|
||||
}
|
||||
|
||||
if( Config.EnableFullResourceLogging )
|
||||
{
|
||||
ResourceLoader.EnableFullLogging();
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
|
|
@ -231,7 +238,7 @@ public class Penumbra : IDalamudPlugin
|
|||
//PathResolver.Dispose();
|
||||
ResourceLogger.Dispose();
|
||||
ResourceLoader.Dispose();
|
||||
|
||||
|
||||
|
||||
ShutdownWebServer();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ using Penumbra.Meta;
|
|||
using Penumbra.Mods;
|
||||
using Penumbra.UI.Custom;
|
||||
using Penumbra.Util;
|
||||
using CharacterUtility = Penumbra.Interop.Structs.CharacterUtility;
|
||||
using ResourceHandle = Penumbra.Interop.Structs.ResourceHandle;
|
||||
using Utf8String = Penumbra.GameData.ByteString.Utf8String;
|
||||
|
||||
|
|
@ -164,12 +165,6 @@ public partial class SettingsInterface
|
|||
PrintValue( "Mod Manager BasePath Exists",
|
||||
manager.BasePath != null ? Directory.Exists( manager.BasePath.FullName ).ToString() : false.ToString() );
|
||||
PrintValue( "Mod Manager Valid", manager.Valid.ToString() );
|
||||
PrintValue( "Mod Manager Temp Path", manager.TempPath?.FullName ?? "NULL" );
|
||||
PrintValue( "Mod Manager Temp Path IsRooted",
|
||||
( !Penumbra.Config.TempDirectory.Any() || Path.IsPathRooted( Penumbra.Config.TempDirectory ) ).ToString() );
|
||||
PrintValue( "Mod Manager Temp Path Exists",
|
||||
manager.TempPath != null ? Directory.Exists( manager.TempPath.FullName ).ToString() : false.ToString() );
|
||||
PrintValue( "Mod Manager Temp Path IsWritable", manager.TempWritable.ToString() );
|
||||
//PrintValue( "Resource Loader Enabled", _penumbra.ResourceLoader.IsEnabled.ToString() );
|
||||
}
|
||||
|
||||
|
|
@ -273,43 +268,6 @@ public partial class SettingsInterface
|
|||
}
|
||||
}
|
||||
|
||||
private static void DrawDebugTabTempFiles()
|
||||
{
|
||||
if( !ImGui.CollapsingHeader( "Temporary Files##Debug" ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( !ImGui.BeginTable( "##tempFileTable", 4, ImGuiTableFlags.SizingFixedFit ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTable );
|
||||
|
||||
foreach( var collection in Penumbra.ModManager.Collections.Collections.Values.Where( c => c.Cache != null ) )
|
||||
{
|
||||
var manip = collection.Cache!.MetaManipulations;
|
||||
var files = ( Dictionary< GamePath, MetaManager.FileInformation >? )manip.GetType()
|
||||
.GetField( "_currentFiles", BindingFlags.NonPublic | BindingFlags.Instance )?.GetValue( manip )
|
||||
?? new Dictionary< GamePath, MetaManager.FileInformation >();
|
||||
|
||||
|
||||
foreach( var (file, info) in files )
|
||||
{
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text( info.CurrentFile?.FullName ?? "None" );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text( file );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text( info.CurrentFile?.Exists ?? false ? "Exists" : "Missing" );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text( info.Changed ? "Data Changed" : "Unchanged" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDebugTabIpc()
|
||||
{
|
||||
if( !ImGui.CollapsingHeader( "IPC##Debug" ) )
|
||||
|
|
@ -369,7 +327,7 @@ public partial class SettingsInterface
|
|||
return;
|
||||
}
|
||||
|
||||
var cache = Penumbra.ModManager.Collections.CurrentCollection.Cache;
|
||||
var cache = Penumbra.ModManager.Collections.CurrentCollection.Cache;
|
||||
if( cache == null || !ImGui.BeginTable( "##MissingFilesDebugList", 1, ImGuiTableFlags.RowBg, -Vector2.UnitX ) )
|
||||
{
|
||||
return;
|
||||
|
|
@ -397,9 +355,9 @@ public partial class SettingsInterface
|
|||
return;
|
||||
}
|
||||
|
||||
_penumbra.ResourceLoader.UpdateDebugInfo();
|
||||
Penumbra.ResourceLoader.UpdateDebugInfo();
|
||||
|
||||
if( _penumbra.ResourceLoader.DebugList.Count == 0
|
||||
if( Penumbra.ResourceLoader.DebugList.Count == 0
|
||||
|| !ImGui.BeginTable( "##ReplacedResourcesDebugList", 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit, -Vector2.UnitX ) )
|
||||
{
|
||||
return;
|
||||
|
|
@ -407,7 +365,7 @@ public partial class SettingsInterface
|
|||
|
||||
using var end = ImGuiRaii.DeferredEnd( ImGui.EndTable );
|
||||
|
||||
foreach( var data in _penumbra.ResourceLoader.DebugList.Values.ToArray() )
|
||||
foreach( var data in Penumbra.ResourceLoader.DebugList.Values.ToArray() )
|
||||
{
|
||||
var refCountManip = data.ManipulatedResource == null ? 0 : data.ManipulatedResource->RefCount;
|
||||
var refCountOrig = data.OriginalResource == null ? 0 : data.OriginalResource->RefCount;
|
||||
|
|
@ -426,6 +384,52 @@ public partial class SettingsInterface
|
|||
}
|
||||
}
|
||||
|
||||
public unsafe void DrawDebugCharacterUtility()
|
||||
{
|
||||
if( !ImGui.CollapsingHeader( "Character Utility##Debug" ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( !ImGui.BeginTable( "##CharacterUtilityDebugList", 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit, -Vector2.UnitX ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var end = ImGuiRaii.DeferredEnd( ImGui.EndTable );
|
||||
|
||||
for( var i = 0; i < CharacterUtility.NumRelevantResources; ++i )
|
||||
{
|
||||
var resource = ( ResourceHandle* )Penumbra.CharacterUtility.Address->Resources[ i ];
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text( $"0x{( ulong )resource:X}" );
|
||||
ImGui.TableNextColumn();
|
||||
ImGuiNative.igTextUnformatted( resource->FileName(), resource->FileName() + resource->FileNameLength );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text( $"0x{resource->GetData().Data:X}" );
|
||||
if( ImGui.IsItemClicked() )
|
||||
{
|
||||
var (data, length) = resource->GetData();
|
||||
ImGui.SetClipboardText( string.Join( " ",
|
||||
new ReadOnlySpan< byte >( ( byte* )data, length ).ToArray().Select( b => b.ToString( "X2" ) ) ) );
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text( $"{resource->GetData().Length}" );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text( $"0x{Penumbra.CharacterUtility.DefaultResources[ i ].Address:X}" );
|
||||
if( ImGui.IsItemClicked() )
|
||||
{
|
||||
ImGui.SetClipboardText( string.Join( " ",
|
||||
new ReadOnlySpan< byte >( ( byte* )Penumbra.CharacterUtility.DefaultResources[ i ].Address,
|
||||
Penumbra.CharacterUtility.DefaultResources[ i ].Size ).ToArray().Select( b => b.ToString( "X2" ) ) ) );
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text( $"{Penumbra.CharacterUtility.DefaultResources[ i ].Size}" );
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void DrawPathResolverDebug()
|
||||
{
|
||||
if( !ImGui.CollapsingHeader( "Path Resolver##Debug" ) )
|
||||
|
|
@ -479,6 +483,14 @@ public partial class SettingsInterface
|
|||
|
||||
using var raii = ImGuiRaii.DeferredEnd( ImGui.EndTabItem );
|
||||
|
||||
if( !ImGui.BeginChild( "##DebugChild", -Vector2.One ) )
|
||||
{
|
||||
ImGui.EndChild();
|
||||
return;
|
||||
}
|
||||
|
||||
raii.Push( ImGui.EndChild );
|
||||
|
||||
DrawDebugTabGeneral();
|
||||
ImGui.NewLine();
|
||||
DrawDebugTabReplacedResources();
|
||||
|
|
@ -491,12 +503,12 @@ public partial class SettingsInterface
|
|||
ImGui.NewLine();
|
||||
DrawPathResolverDebug();
|
||||
ImGui.NewLine();
|
||||
DrawDebugCharacterUtility();
|
||||
ImGui.NewLine();
|
||||
DrawDebugTabRedraw();
|
||||
ImGui.NewLine();
|
||||
DrawDebugTabPlayers();
|
||||
ImGui.NewLine();
|
||||
DrawDebugTabTempFiles();
|
||||
ImGui.NewLine();
|
||||
DrawDebugTabIpc();
|
||||
ImGui.NewLine();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,8 +192,9 @@ public partial class SettingsInterface
|
|||
}
|
||||
else if( ( row -= activeResolved ) < activeMeta )
|
||||
{
|
||||
var (manip, mod) = activeCollection!.MetaManipulations.Manipulations.ElementAt( row );
|
||||
DrawLine( manip.ToString(), mod.Data.Meta.Name );
|
||||
// TODO
|
||||
//var (manip, mod) = activeCollection!.MetaManipulations.Manipulations.ElementAt( row );
|
||||
DrawLine( 0.ToString(), 0.ToString() );
|
||||
}
|
||||
else if( ( row -= activeMeta ) < forcedResolved )
|
||||
{
|
||||
|
|
@ -202,9 +203,10 @@ public partial class SettingsInterface
|
|||
}
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
row -= forcedResolved;
|
||||
var (manip, mod) = forcedCollection!.MetaManipulations.Manipulations.ElementAt( row );
|
||||
DrawLine( manip.ToString(), mod.Data.Meta.Name );
|
||||
//var (manip, mod) = forcedCollection!.MetaManipulations.Manipulations.ElementAt( row );
|
||||
DrawLine( 0.ToString(), 0.ToString() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -426,8 +426,7 @@ public partial class SettingsInterface
|
|||
foreach( var collection in modManager.Collections.Collections.Values
|
||||
.Where( c => c.Cache != null && c.Settings[ Mod!.Data.BasePath.Name ].Enabled ) )
|
||||
{
|
||||
collection.CalculateEffectiveFileList( modManager.TempPath, false,
|
||||
collection == modManager.Collections.ActiveCollection );
|
||||
collection.CalculateEffectiveFileList( false, collection == modManager.Collections.ActiveCollection );
|
||||
}
|
||||
|
||||
// If the mod is enabled in the current collection, its conflicts may have changed.
|
||||
|
|
|
|||
|
|
@ -1,18 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using ImGuiNET;
|
||||
using Lumina.Data.Files;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.UI.Custom;
|
||||
using Penumbra.Util;
|
||||
using ObjectType = Penumbra.GameData.Enums.ObjectType;
|
||||
|
||||
namespace Penumbra.UI
|
||||
|
|
@ -255,7 +249,6 @@ namespace Penumbra.UI
|
|||
|
||||
private bool DrawGmpRow( int manipIdx, IList< MetaManipulation > list )
|
||||
{
|
||||
var defaults = ( GmpEntry )Penumbra.MetaDefaults.GetDefaultValue( list[ manipIdx ] )!;
|
||||
var ret = false;
|
||||
//var id = list[ manipIdx ].GmpIdentifier;
|
||||
//var val = list[ manipIdx ].GmpValue;
|
||||
|
|
@ -365,7 +358,6 @@ namespace Penumbra.UI
|
|||
|
||||
private bool DrawEqdpRow( int manipIdx, IList< MetaManipulation > list )
|
||||
{
|
||||
var defaults = ( EqdpEntry )Penumbra.MetaDefaults.GetDefaultValue( list[ manipIdx ] )!;
|
||||
var ret = false;
|
||||
//var id = list[ manipIdx ].EqdpIdentifier;
|
||||
//var val = list[ manipIdx ].EqdpValue;
|
||||
|
|
@ -402,7 +394,6 @@ namespace Penumbra.UI
|
|||
|
||||
private bool DrawEstRow( int manipIdx, IList< MetaManipulation > list )
|
||||
{
|
||||
var defaults = ( ushort )Penumbra.MetaDefaults.GetDefaultValue( list[ manipIdx ] )!;
|
||||
var ret = false;
|
||||
//var id = list[ manipIdx ].EstIdentifier;
|
||||
//var val = list[ manipIdx ].EstValue;
|
||||
|
|
@ -434,7 +425,6 @@ namespace Penumbra.UI
|
|||
|
||||
private bool DrawImcRow( int manipIdx, IList< MetaManipulation > list )
|
||||
{
|
||||
var defaults = ( ImcFile.ImageChangeData )Penumbra.MetaDefaults.GetDefaultValue( list[ manipIdx ] )!;
|
||||
var ret = false;
|
||||
//var id = list[ manipIdx ].ImcIdentifier;
|
||||
//var val = list[ manipIdx ].ImcValue;
|
||||
|
|
@ -493,7 +483,6 @@ namespace Penumbra.UI
|
|||
|
||||
private bool DrawRspRow( int manipIdx, IList< MetaManipulation > list )
|
||||
{
|
||||
var defaults = ( float )Penumbra.MetaDefaults.GetDefaultValue( list[ manipIdx ] )!;
|
||||
var ret = false;
|
||||
//var id = list[ manipIdx ].RspIdentifier;
|
||||
//var val = list[ manipIdx ].RspValue;
|
||||
|
|
|
|||
|
|
@ -529,8 +529,7 @@ public partial class SettingsInterface
|
|||
var collection = Penumbra.ModManager.Collections.CurrentCollection;
|
||||
if( collection.Cache != null )
|
||||
{
|
||||
collection.CalculateEffectiveFileList( Penumbra.ModManager.TempPath, metaManips,
|
||||
collection == Penumbra.ModManager.Collections.ActiveCollection );
|
||||
collection.CalculateEffectiveFileList( metaManips, collection == Penumbra.ModManager.Collections.ActiveCollection );
|
||||
}
|
||||
|
||||
collection.Save();
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ public partial class SettingsInterface
|
|||
if( ImGui.IsItemClicked() )
|
||||
{
|
||||
var data = ( ( Interop.Structs.ResourceHandle* )r )->GetData();
|
||||
ImGui.SetClipboardText( ((IntPtr)( ( Interop.Structs.ResourceHandle* )r )->Data->VTable).ToString("X") + string.Join( " ",
|
||||
ImGui.SetClipboardText( string.Join( " ",
|
||||
new ReadOnlySpan< byte >( ( byte* )data.Data, data.Length ).ToArray().Select( b => b.ToString( "X2" ) ) ) );
|
||||
//ImGuiNative.igSetClipboardText( ( byte* )Structs.ResourceHandle.GetData( ( IntPtr )r ) );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ public partial class SettingsInterface
|
|||
private readonly Configuration _config;
|
||||
private bool _configChanged;
|
||||
private string _newModDirectory;
|
||||
private string _newTempDirectory;
|
||||
|
||||
|
||||
public TabSettings( SettingsInterface ui )
|
||||
{
|
||||
|
|
@ -33,7 +31,6 @@ public partial class SettingsInterface
|
|||
_config = Penumbra.Config;
|
||||
_configChanged = false;
|
||||
_newModDirectory = _config.ModDirectory;
|
||||
_newTempDirectory = _config.TempDirectory;
|
||||
}
|
||||
|
||||
private static bool DrawPressEnterWarning( string old )
|
||||
|
|
@ -91,33 +88,6 @@ public partial class SettingsInterface
|
|||
}
|
||||
}
|
||||
|
||||
private void DrawTempFolder()
|
||||
{
|
||||
ImGui.BeginGroup();
|
||||
ImGui.SetNextItemWidth( SettingsMenu.InputTextWidth );
|
||||
var save = ImGui.InputText( "Temp Directory", ref _newTempDirectory, 255, ImGuiInputTextFlags.EnterReturnsTrue );
|
||||
ImGui.SameLine();
|
||||
ImGuiComponents.HelpMarker( "This is where Penumbra will store temporary meta manipulation files.\n"
|
||||
+ "Leave this blank if you have no reason not to.\n"
|
||||
+ "A directory 'penumbrametatmp' will be created as a sub-directory to the specified directory.\n"
|
||||
+ "If none is specified (i.e. this is blank) this directory will be created in the root directory instead.\n" );
|
||||
ImGui.SameLine();
|
||||
var modManager = Penumbra.ModManager;
|
||||
DrawOpenDirectoryButton( 1, modManager.TempPath, modManager.TempWritable );
|
||||
ImGui.EndGroup();
|
||||
|
||||
if( _newTempDirectory == _config.TempDirectory )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( save || DrawPressEnterWarning( _config.TempDirectory ) )
|
||||
{
|
||||
modManager.SetTempDirectory( _newTempDirectory );
|
||||
_newTempDirectory = _config.TempDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawRediscoverButton()
|
||||
{
|
||||
if( ImGui.Button( "Rediscover Mods" ) )
|
||||
|
|
@ -326,11 +296,11 @@ public partial class SettingsInterface
|
|||
{
|
||||
if( tmp )
|
||||
{
|
||||
_base._penumbra.ResourceLoader.EnableFullLogging();
|
||||
Penumbra.ResourceLoader.EnableFullLogging();
|
||||
}
|
||||
else
|
||||
{
|
||||
_base._penumbra.ResourceLoader.DisableFullLogging();
|
||||
Penumbra.ResourceLoader.DisableFullLogging();
|
||||
}
|
||||
|
||||
_config.EnableFullResourceLogging = tmp;
|
||||
|
|
@ -348,11 +318,11 @@ public partial class SettingsInterface
|
|||
{
|
||||
if( tmp )
|
||||
{
|
||||
_base._penumbra.ResourceLoader.EnableDebug();
|
||||
Penumbra.ResourceLoader.EnableDebug();
|
||||
}
|
||||
else
|
||||
{
|
||||
_base._penumbra.ResourceLoader.DisableDebug();
|
||||
Penumbra.ResourceLoader.DisableDebug();
|
||||
}
|
||||
|
||||
_config.DebugMode = tmp;
|
||||
|
|
@ -388,7 +358,6 @@ public partial class SettingsInterface
|
|||
|
||||
private void DrawAdvancedSettings()
|
||||
{
|
||||
DrawTempFolder();
|
||||
DrawRequestedResourceLogging();
|
||||
DrawDisableSoundStreamingBox();
|
||||
DrawLogLoadedFilesBox();
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ public partial class SettingsInterface : IDisposable
|
|||
var current = modManager.Collections.CurrentCollection;
|
||||
if( current.Cache != null )
|
||||
{
|
||||
current.CalculateEffectiveFileList( modManager.TempPath, recalculateMeta,
|
||||
current.CalculateEffectiveFileList( recalculateMeta,
|
||||
current == modManager.Collections.ActiveCollection );
|
||||
_menu.InstalledTab.Selector.Cache.TriggerFilterReset();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue