mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-01-02 05:43:42 +01:00
Tmp for Mod2
This commit is contained in:
parent
069ae772a5
commit
48e442a9fd
30 changed files with 697 additions and 252 deletions
79
Penumbra/Mods/Subclasses/IModGroup.cs
Normal file
79
Penumbra/Mods/Subclasses/IModGroup.cs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Dalamud.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
public interface IModGroup : IEnumerable< ISubMod >
|
||||
{
|
||||
public string Name { get; }
|
||||
public string Description { get; }
|
||||
public SelectType Type { get; }
|
||||
public int Priority { get; }
|
||||
|
||||
public int OptionPriority( Index optionIdx );
|
||||
|
||||
public ISubMod this[ Index idx ] { get; }
|
||||
|
||||
public int Count { get; }
|
||||
|
||||
public bool IsOption
|
||||
=> Type switch
|
||||
{
|
||||
SelectType.Single => Count > 1,
|
||||
SelectType.Multi => Count > 0,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
public string FileName( DirectoryInfo basePath )
|
||||
=> Path.Combine( basePath.FullName, $"group_{Name.RemoveInvalidPathSymbols().ToLowerInvariant()}.json" );
|
||||
|
||||
public void DeleteFile( DirectoryInfo basePath )
|
||||
{
|
||||
var file = FileName( basePath );
|
||||
if( !File.Exists( file ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.Delete( file );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not delete file {file}:\n{e}" );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveModGroup( IModGroup group, DirectoryInfo basePath )
|
||||
{
|
||||
var file = group.FileName( basePath );
|
||||
using var s = File.Exists( file ) ? File.Open( file, FileMode.Truncate ) : File.Open( file, FileMode.CreateNew );
|
||||
using var writer = new StreamWriter( s );
|
||||
using var j = new JsonTextWriter( writer ) { Formatting = Formatting.Indented };
|
||||
var serializer = new JsonSerializer { Formatting = Formatting.Indented };
|
||||
j.WriteStartObject();
|
||||
j.WritePropertyName( nameof( group.Name ) );
|
||||
j.WriteValue( group.Name );
|
||||
j.WritePropertyName( nameof( group.Description ) );
|
||||
j.WriteValue( group.Description );
|
||||
j.WritePropertyName( nameof( group.Priority ) );
|
||||
j.WriteValue( group.Priority );
|
||||
j.WritePropertyName( nameof( Type ) );
|
||||
j.WriteValue( group.Type.ToString() );
|
||||
j.WritePropertyName( "Options" );
|
||||
j.WriteStartArray();
|
||||
for( var idx = 0; idx < group.Count; ++idx )
|
||||
{
|
||||
ISubMod.WriteSubMod( j, serializer, group[ idx ], basePath, group.Type == SelectType.Multi ? group.OptionPriority( idx ) : null );
|
||||
}
|
||||
|
||||
j.WriteEndArray();
|
||||
j.WriteEndObject();
|
||||
}
|
||||
}
|
||||
53
Penumbra/Mods/Subclasses/ISubMod.cs
Normal file
53
Penumbra/Mods/Subclasses/ISubMod.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
public interface ISubMod
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public IReadOnlyDictionary< Utf8GamePath, FullPath > Files { get; }
|
||||
public IReadOnlyDictionary< Utf8GamePath, FullPath > FileSwaps { get; }
|
||||
public IReadOnlySet< MetaManipulation > Manipulations { get; }
|
||||
|
||||
public static void WriteSubMod( JsonWriter j, JsonSerializer serializer, ISubMod mod, DirectoryInfo basePath, int? priority )
|
||||
{
|
||||
j.WriteStartObject();
|
||||
j.WritePropertyName( nameof( Name ) );
|
||||
j.WriteValue( mod.Name );
|
||||
if( priority != null )
|
||||
{
|
||||
j.WritePropertyName( nameof( IModGroup.Priority ) );
|
||||
j.WriteValue( priority.Value );
|
||||
}
|
||||
|
||||
j.WritePropertyName( nameof( mod.Files ) );
|
||||
j.WriteStartObject();
|
||||
foreach( var (gamePath, file) in mod.Files )
|
||||
{
|
||||
if( file.ToRelPath( basePath, out var relPath ) )
|
||||
{
|
||||
j.WritePropertyName( gamePath.ToString() );
|
||||
j.WriteValue( relPath.ToString() );
|
||||
}
|
||||
}
|
||||
|
||||
j.WriteEndObject();
|
||||
j.WritePropertyName( nameof( mod.FileSwaps ) );
|
||||
j.WriteStartObject();
|
||||
foreach( var (gamePath, file) in mod.FileSwaps )
|
||||
{
|
||||
j.WritePropertyName( gamePath.ToString() );
|
||||
j.WriteValue( file.ToString() );
|
||||
}
|
||||
|
||||
j.WriteEndObject();
|
||||
j.WritePropertyName( nameof( mod.Manipulations ) );
|
||||
serializer.Serialize( j, mod.Manipulations );
|
||||
j.WriteEndObject();
|
||||
}
|
||||
}
|
||||
67
Penumbra/Mods/Subclasses/Mod2.Files.MultiModGroup.cs
Normal file
67
Penumbra/Mods/Subclasses/Mod2.Files.MultiModGroup.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
public partial class Mod2
|
||||
{
|
||||
private sealed class MultiModGroup : IModGroup
|
||||
{
|
||||
public SelectType Type
|
||||
=> SelectType.Multi;
|
||||
|
||||
public string Name { get; set; } = "Group";
|
||||
public string Description { get; set; } = "A non-exclusive group of settings.";
|
||||
public int Priority { get; set; }
|
||||
|
||||
public int OptionPriority( Index idx )
|
||||
=> PrioritizedOptions[ idx ].Priority;
|
||||
|
||||
public ISubMod this[ Index idx ]
|
||||
=> PrioritizedOptions[ idx ].Mod;
|
||||
|
||||
[JsonIgnore]
|
||||
public int Count
|
||||
=> PrioritizedOptions.Count;
|
||||
|
||||
public readonly List< (SubMod Mod, int Priority) > PrioritizedOptions = new();
|
||||
|
||||
public IEnumerator< ISubMod > GetEnumerator()
|
||||
=> PrioritizedOptions.Select( o => o.Mod ).GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public static MultiModGroup? Load( JObject json, DirectoryInfo basePath )
|
||||
{
|
||||
var options = json[ "Options" ];
|
||||
var ret = new MultiModGroup()
|
||||
{
|
||||
Name = json[ nameof( Name ) ]?.ToObject< string >() ?? string.Empty,
|
||||
Description = json[ nameof( Description ) ]?.ToObject< string >() ?? string.Empty,
|
||||
Priority = json[ nameof( Priority ) ]?.ToObject< int >() ?? 0,
|
||||
};
|
||||
if( ret.Name.Length == 0 )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if( options != null )
|
||||
{
|
||||
foreach( var child in options.Children() )
|
||||
{
|
||||
var subMod = new SubMod();
|
||||
subMod.Load( basePath, child, out var priority );
|
||||
ret.PrioritizedOptions.Add( ( subMod, priority ) );
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
66
Penumbra/Mods/Subclasses/Mod2.Files.SingleModGroup.cs
Normal file
66
Penumbra/Mods/Subclasses/Mod2.Files.SingleModGroup.cs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
public partial class Mod2
|
||||
{
|
||||
private sealed class SingleModGroup : IModGroup
|
||||
{
|
||||
public SelectType Type
|
||||
=> SelectType.Single;
|
||||
|
||||
public string Name { get; set; } = "Option";
|
||||
public string Description { get; set; } = "A mutually exclusive group of settings.";
|
||||
public int Priority { get; set; }
|
||||
|
||||
public readonly List< SubMod > OptionData = new();
|
||||
|
||||
public int OptionPriority( Index _ )
|
||||
=> Priority;
|
||||
|
||||
public ISubMod this[ Index idx ]
|
||||
=> OptionData[ idx ];
|
||||
|
||||
[JsonIgnore]
|
||||
public int Count
|
||||
=> OptionData.Count;
|
||||
|
||||
public IEnumerator< ISubMod > GetEnumerator()
|
||||
=> OptionData.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public static SingleModGroup? Load( JObject json, DirectoryInfo basePath )
|
||||
{
|
||||
var options = json[ "Options" ];
|
||||
var ret = new SingleModGroup
|
||||
{
|
||||
Name = json[ nameof( Name ) ]?.ToObject< string >() ?? string.Empty,
|
||||
Description = json[ nameof( Description ) ]?.ToObject< string >() ?? string.Empty,
|
||||
Priority = json[ nameof( Priority ) ]?.ToObject< int >() ?? 0,
|
||||
};
|
||||
if( ret.Name.Length == 0 )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if( options != null )
|
||||
{
|
||||
foreach( var child in options.Children() )
|
||||
{
|
||||
var subMod = new SubMod();
|
||||
subMod.Load( basePath, child, out _ );
|
||||
ret.OptionData.Add( subMod );
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
117
Penumbra/Mods/Subclasses/Mod2.Files.SubMod.cs
Normal file
117
Penumbra/Mods/Subclasses/Mod2.Files.SubMod.cs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dalamud.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
public partial class Mod2
|
||||
{
|
||||
private string DefaultFile
|
||||
=> Path.Combine( BasePath.FullName, "default_mod.json" );
|
||||
|
||||
private void SaveDefaultMod()
|
||||
{
|
||||
var defaultFile = DefaultFile;
|
||||
|
||||
using var stream = File.Exists( defaultFile )
|
||||
? File.Open( defaultFile, FileMode.Truncate )
|
||||
: File.Open( defaultFile, FileMode.CreateNew );
|
||||
|
||||
using var w = new StreamWriter( stream );
|
||||
using var j = new JsonTextWriter( w );
|
||||
j.Formatting = Formatting.Indented;
|
||||
var serializer = new JsonSerializer
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
};
|
||||
ISubMod.WriteSubMod( j, serializer, _default, BasePath, 0 );
|
||||
}
|
||||
|
||||
private void LoadDefaultOption()
|
||||
{
|
||||
var defaultFile = DefaultFile;
|
||||
try
|
||||
{
|
||||
if( !File.Exists( defaultFile ) )
|
||||
{
|
||||
_default.Load( BasePath, new JObject(), out _ );
|
||||
}
|
||||
else
|
||||
{
|
||||
_default.Load( BasePath, JObject.Parse( File.ReadAllText( defaultFile ) ), out _ );
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not parse default file for {Name}:\n{e}" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private sealed class SubMod : ISubMod
|
||||
{
|
||||
public string Name { get; set; } = "Default";
|
||||
|
||||
public readonly Dictionary< Utf8GamePath, FullPath > FileData = new();
|
||||
public readonly Dictionary< Utf8GamePath, FullPath > FileSwapData = new();
|
||||
public readonly HashSet< MetaManipulation > ManipulationData = new();
|
||||
|
||||
public IReadOnlyDictionary< Utf8GamePath, FullPath > Files
|
||||
=> FileData;
|
||||
|
||||
public IReadOnlyDictionary< Utf8GamePath, FullPath > FileSwaps
|
||||
=> FileSwapData;
|
||||
|
||||
public IReadOnlySet< MetaManipulation > Manipulations
|
||||
=> ManipulationData;
|
||||
|
||||
public void Load( DirectoryInfo basePath, JToken json, out int priority )
|
||||
{
|
||||
FileData.Clear();
|
||||
FileSwapData.Clear();
|
||||
ManipulationData.Clear();
|
||||
|
||||
Name = json[ nameof( ISubMod.Name ) ]?.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() )
|
||||
{
|
||||
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() )
|
||||
{
|
||||
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 >() ) )
|
||||
{
|
||||
ManipulationData.Add( s );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
75
Penumbra/Mods/Subclasses/ModSettings.cs
Normal file
75
Penumbra/Mods/Subclasses/ModSettings.cs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
// Contains the settings for a given mod.
|
||||
public class ModSettings
|
||||
{
|
||||
public static readonly ModSettings Empty = new();
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
public int Priority { get; set; }
|
||||
public Dictionary< string, int > Settings { get; set; } = new();
|
||||
|
||||
// For backwards compatibility
|
||||
private Dictionary< string, int > Conf
|
||||
{
|
||||
set => Settings = value;
|
||||
}
|
||||
|
||||
public ModSettings DeepCopy()
|
||||
{
|
||||
var settings = new ModSettings
|
||||
{
|
||||
Enabled = Enabled,
|
||||
Priority = Priority,
|
||||
Settings = Settings.ToDictionary( kvp => kvp.Key, kvp => kvp.Value ),
|
||||
};
|
||||
return settings;
|
||||
}
|
||||
|
||||
public static ModSettings DefaultSettings( ModMeta meta )
|
||||
{
|
||||
return new ModSettings
|
||||
{
|
||||
Enabled = false,
|
||||
Priority = 0,
|
||||
Settings = meta.Groups.ToDictionary( kvp => kvp.Key, _ => 0 ),
|
||||
};
|
||||
}
|
||||
|
||||
public bool FixSpecificSetting( string name, ModMeta meta )
|
||||
{
|
||||
if( !meta.Groups.TryGetValue( name, out var group ) )
|
||||
{
|
||||
return Settings.Remove( name );
|
||||
}
|
||||
|
||||
if( Settings.TryGetValue( name, out var oldSetting ) )
|
||||
{
|
||||
Settings[ name ] = group.SelectionType switch
|
||||
{
|
||||
SelectType.Single => Math.Min( Math.Max( oldSetting, 0 ), group.Options.Count - 1 ),
|
||||
SelectType.Multi => Math.Min( Math.Max( oldSetting, 0 ), ( 1 << group.Options.Count ) - 1 ),
|
||||
_ => Settings[ group.GroupName ],
|
||||
};
|
||||
return oldSetting != Settings[ group.GroupName ];
|
||||
}
|
||||
|
||||
Settings[ name ] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool FixInvalidSettings( ModMeta meta )
|
||||
{
|
||||
if( meta.Groups.Count == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Settings.Keys.ToArray().Union( meta.Groups.Keys )
|
||||
.Aggregate( false, ( current, name ) => current | FixSpecificSetting( name, meta ) );
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue