mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
move desc to own tab, invert load order setting, normalise line endings
This commit is contained in:
parent
fa9e4d7dcc
commit
25ac5d1999
14 changed files with 476 additions and 414 deletions
|
|
@ -1,4 +1,3 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using EmbedIO;
|
using EmbedIO;
|
||||||
using EmbedIO.Routing;
|
using EmbedIO.Routing;
|
||||||
|
|
@ -25,7 +24,7 @@ namespace Penumbra.API
|
||||||
x.FolderName,
|
x.FolderName,
|
||||||
x.Mod.Meta,
|
x.Mod.Meta,
|
||||||
BasePath = x.Mod.ModBasePath.FullName,
|
BasePath = x.Mod.ModBasePath.FullName,
|
||||||
Files = x.Mod.ModFiles.Select( x => x.FullName )
|
Files = x.Mod.ModFiles.Select( fi => fi.FullName )
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,16 +12,18 @@ namespace Penumbra
|
||||||
|
|
||||||
public bool IsEnabled { get; set; } = true;
|
public bool IsEnabled { get; set; } = true;
|
||||||
|
|
||||||
public bool ShowAdvanced { get; set; } = false;
|
public bool ShowAdvanced { get; set; }
|
||||||
|
|
||||||
public bool DisableFileSystemNotifications { get; set; } = false;
|
public bool DisableFileSystemNotifications { get; set; }
|
||||||
|
|
||||||
public bool EnableHttpApi { get; set; } = false;
|
public bool EnableHttpApi { get; set; }
|
||||||
|
|
||||||
public string CurrentCollection { get; set; } = @"D:/ffxiv/fs_mods/";
|
public string CurrentCollection { get; set; } = @"D:/ffxiv/fs_mods/";
|
||||||
|
|
||||||
public List< string > ModCollections { get; set; } = new();
|
public List< string > ModCollections { get; set; } = new();
|
||||||
|
|
||||||
|
public bool InvertModListOrder { get; set; }
|
||||||
|
|
||||||
// the below exist just to make saving less cumbersome
|
// the below exist just to make saving less cumbersome
|
||||||
|
|
||||||
[NonSerialized]
|
[NonSerialized]
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
@ -49,15 +46,15 @@ namespace Penumbra
|
||||||
|
|
||||||
public class HiddenForm : Form
|
public class HiddenForm : Form
|
||||||
{
|
{
|
||||||
private readonly CommonDialog form;
|
private readonly CommonDialog _form;
|
||||||
private readonly IWin32Window owner;
|
private readonly IWin32Window _owner;
|
||||||
private readonly TaskCompletionSource< DialogResult > taskSource;
|
private readonly TaskCompletionSource< DialogResult > _taskSource;
|
||||||
|
|
||||||
public HiddenForm( CommonDialog form, IWin32Window owner, TaskCompletionSource< DialogResult > taskSource )
|
public HiddenForm( CommonDialog form, IWin32Window owner, TaskCompletionSource< DialogResult > taskSource )
|
||||||
{
|
{
|
||||||
this.form = form;
|
this._form = form;
|
||||||
this.owner = owner;
|
this._owner = owner;
|
||||||
this.taskSource = taskSource;
|
this._taskSource = taskSource;
|
||||||
|
|
||||||
Opacity = 0;
|
Opacity = 0;
|
||||||
FormBorderStyle = FormBorderStyle.None;
|
FormBorderStyle = FormBorderStyle.None;
|
||||||
|
|
@ -72,12 +69,12 @@ namespace Penumbra
|
||||||
Hide();
|
Hide();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = form.ShowDialog( owner );
|
var result = _form.ShowDialog( _owner );
|
||||||
taskSource.SetResult( result );
|
_taskSource.SetResult( result );
|
||||||
}
|
}
|
||||||
catch( Exception e )
|
catch( Exception e )
|
||||||
{
|
{
|
||||||
taskSource.SetException( e );
|
_taskSource.SetException( e );
|
||||||
}
|
}
|
||||||
|
|
||||||
Close();
|
Close();
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ namespace Penumbra.Game
|
||||||
{
|
{
|
||||||
public class GameUtils
|
public class GameUtils
|
||||||
{
|
{
|
||||||
private readonly DalamudPluginInterface _pluginInterface;
|
|
||||||
|
|
||||||
[Function( CallingConventions.Microsoft )]
|
[Function( CallingConventions.Microsoft )]
|
||||||
public unsafe delegate void* LoadPlayerResourcesPrototype( IntPtr pResourceManager );
|
public unsafe delegate void* LoadPlayerResourcesPrototype( IntPtr pResourceManager );
|
||||||
|
|
||||||
|
|
@ -25,9 +23,7 @@ namespace Penumbra.Game
|
||||||
|
|
||||||
public GameUtils( DalamudPluginInterface pluginInterface )
|
public GameUtils( DalamudPluginInterface pluginInterface )
|
||||||
{
|
{
|
||||||
_pluginInterface = pluginInterface;
|
var scanner = pluginInterface.TargetModuleScanner;
|
||||||
|
|
||||||
var scanner = _pluginInterface.TargetModuleScanner;
|
|
||||||
|
|
||||||
var loadPlayerResourcesAddress =
|
var loadPlayerResourcesAddress =
|
||||||
scanner.ScanText(
|
scanner.ScanText(
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@ namespace Penumbra.Importer
|
||||||
private readonly DirectoryInfo _outDirectory;
|
private readonly DirectoryInfo _outDirectory;
|
||||||
|
|
||||||
private const string TempFileName = "textools-import";
|
private const string TempFileName = "textools-import";
|
||||||
private readonly string _resolvedTempFilePath = null;
|
private readonly string _resolvedTempFilePath;
|
||||||
|
|
||||||
public ImporterState State { get; private set; }
|
public ImporterState State { get; private set; }
|
||||||
|
|
||||||
public long TotalProgress { get; private set; }
|
public long TotalProgress { get; private set; }
|
||||||
public long CurrentProgress { get; private set; }
|
public long CurrentProgress { get; private set; }
|
||||||
|
|
||||||
|
|
@ -49,7 +49,7 @@ namespace Penumbra.Importer
|
||||||
public void ImportModPack( FileInfo modPackFile )
|
public void ImportModPack( FileInfo modPackFile )
|
||||||
{
|
{
|
||||||
CurrentModPack = modPackFile.Name;
|
CurrentModPack = modPackFile.Name;
|
||||||
|
|
||||||
switch( modPackFile.Extension )
|
switch( modPackFile.Extension )
|
||||||
{
|
{
|
||||||
case ".ttmp":
|
case ".ttmp":
|
||||||
|
|
@ -74,7 +74,7 @@ namespace Penumbra.Importer
|
||||||
private SqPackStream GetMagicSqPackDeleterStream( ZipFile file, string entryName )
|
private SqPackStream GetMagicSqPackDeleterStream( ZipFile file, string entryName )
|
||||||
{
|
{
|
||||||
State = ImporterState.WritingPackToDisk;
|
State = ImporterState.WritingPackToDisk;
|
||||||
|
|
||||||
// write shitty zip garbage to disk
|
// write shitty zip garbage to disk
|
||||||
var entry = file.GetEntry( entryName );
|
var entry = file.GetEntry( entryName );
|
||||||
using var s = file.GetInputStream( entry );
|
using var s = file.GetInputStream( entry );
|
||||||
|
|
@ -215,32 +215,32 @@ namespace Penumbra.Importer
|
||||||
from modGroup in modPackPage.ModGroups
|
from modGroup in modPackPage.ModGroups
|
||||||
from option in modGroup.OptionList
|
from option in modGroup.OptionList
|
||||||
select option )
|
select option )
|
||||||
{
|
{
|
||||||
var OptionFolder = new DirectoryInfo(Path.Combine(newModFolder.FullName, option.Name));
|
var optionFolder = new DirectoryInfo( Path.Combine( newModFolder.FullName, option.Name ) );
|
||||||
ExtractSimpleModList(OptionFolder, option.ModsJsons, modData );
|
ExtractSimpleModList( optionFolder, option.ModsJsons, modData );
|
||||||
AddMeta(OptionFolder, newModFolder, modMeta, option.Name);
|
AddMeta( optionFolder, newModFolder, modMeta, option.Name );
|
||||||
}
|
}
|
||||||
|
|
||||||
File.WriteAllText(
|
File.WriteAllText(
|
||||||
Path.Combine( newModFolder.FullName, "meta.json" ),
|
Path.Combine( newModFolder.FullName, "meta.json" ),
|
||||||
JsonConvert.SerializeObject( modMeta, Formatting.Indented )
|
JsonConvert.SerializeObject( modMeta, Formatting.Indented )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddMeta(DirectoryInfo optionFolder, DirectoryInfo baseFolder, ModMeta meta, string optionName)
|
void AddMeta( DirectoryInfo optionFolder, DirectoryInfo baseFolder, ModMeta meta, string optionName )
|
||||||
{
|
{
|
||||||
var optionFolderLength = optionFolder.FullName.Length;
|
var optionFolderLength = optionFolder.FullName.Length;
|
||||||
var baseFolderLength = baseFolder.FullName.Length;
|
var baseFolderLength = baseFolder.FullName.Length;
|
||||||
foreach( var dir in optionFolder.EnumerateDirectories() )
|
foreach( var dir in optionFolder.EnumerateDirectories() )
|
||||||
{
|
{
|
||||||
foreach( var file in dir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) )
|
foreach( var file in dir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) )
|
||||||
{
|
{
|
||||||
meta.Groups.AddFileToOtherGroups(optionName
|
meta.Groups.AddFileToOtherGroups( optionName
|
||||||
, file.FullName.Substring(baseFolderLength).TrimStart('\\')
|
, file.FullName.Substring( baseFolderLength ).TrimStart( '\\' )
|
||||||
, file.FullName.Substring(optionFolderLength).TrimStart('\\').Replace('\\', '/'));
|
, file.FullName.Substring( optionFolderLength ).TrimStart( '\\' ).Replace( '\\', '/' ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ImportMetaModPack( FileInfo file )
|
private void ImportMetaModPack( FileInfo file )
|
||||||
{
|
{
|
||||||
|
|
@ -250,12 +250,12 @@ namespace Penumbra.Importer
|
||||||
private void ExtractSimpleModList( DirectoryInfo outDirectory, IEnumerable< SimpleMod > mods, SqPackStream dataStream )
|
private void ExtractSimpleModList( DirectoryInfo outDirectory, IEnumerable< SimpleMod > mods, SqPackStream dataStream )
|
||||||
{
|
{
|
||||||
State = ImporterState.ExtractingModFiles;
|
State = ImporterState.ExtractingModFiles;
|
||||||
|
|
||||||
// haha allocation go brr
|
// haha allocation go brr
|
||||||
var wtf = mods.ToList();
|
var wtf = mods.ToList();
|
||||||
|
|
||||||
TotalProgress = wtf.LongCount();
|
TotalProgress = wtf.LongCount();
|
||||||
|
|
||||||
// Extract each SimpleMod into the new mod folder
|
// Extract each SimpleMod into the new mod folder
|
||||||
foreach( var simpleMod in wtf )
|
foreach( var simpleMod in wtf )
|
||||||
{
|
{
|
||||||
|
|
@ -264,7 +264,7 @@ namespace Penumbra.Importer
|
||||||
// do we increment here too???? can this even happen?????
|
// do we increment here too???? can this even happen?????
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtractMod( outDirectory, simpleMod, dataStream );
|
ExtractMod( outDirectory, simpleMod, dataStream );
|
||||||
CurrentProgress++;
|
CurrentProgress++;
|
||||||
}
|
}
|
||||||
|
|
@ -296,12 +296,12 @@ namespace Penumbra.Importer
|
||||||
{
|
{
|
||||||
// Model file header LOD num
|
// Model file header LOD num
|
||||||
mdl[ 64 ] = 1;
|
mdl[ 64 ] = 1;
|
||||||
|
|
||||||
// Model header LOD num
|
// Model header LOD num
|
||||||
var stackSize = BitConverter.ToUInt32( mdl, 4 );
|
var stackSize = BitConverter.ToUInt32( mdl, 4 );
|
||||||
var runtimeBegin = stackSize + 0x44;
|
var runtimeBegin = stackSize + 0x44;
|
||||||
var stringsLengthOffset = runtimeBegin + 4;
|
var stringsLengthOffset = runtimeBegin + 4;
|
||||||
var stringsLength = BitConverter.ToUInt32( mdl, (int) stringsLengthOffset );
|
var stringsLength = BitConverter.ToUInt32( mdl, ( int )stringsLengthOffset );
|
||||||
var modelHeaderStart = stringsLengthOffset + stringsLength + 4;
|
var modelHeaderStart = stringsLengthOffset + stringsLength + 4;
|
||||||
var modelHeaderLodOffset = 22;
|
var modelHeaderLodOffset = 22;
|
||||||
mdl[ modelHeaderStart + modelHeaderLodOffset ] = 1;
|
mdl[ modelHeaderStart + modelHeaderLodOffset ] = 1;
|
||||||
|
|
|
||||||
|
|
@ -1,157 +1,189 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Penumbra.Models
|
namespace Penumbra.Models
|
||||||
{
|
{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class GroupInformation : ISerializable
|
public class GroupInformation : ISerializable
|
||||||
{
|
{
|
||||||
|
// This class is just used as a temp class while (de)-serializing.
|
||||||
// This class is just used as a temp class while (de)-serializing.
|
// It converts the flags into lists and back.
|
||||||
// It converts the flags into lists and back.
|
[Serializable]
|
||||||
[Serializable]
|
private class GroupDescription : ISerializable
|
||||||
private class GroupDescription : ISerializable
|
{
|
||||||
{
|
public GroupDescription( GroupInformation info, (string, uint, uint, ulong) vars )
|
||||||
public GroupDescription(GroupInformation info, (string, uint, uint, ulong) vars)
|
{
|
||||||
{
|
GamePath = vars.Item1;
|
||||||
GamePath = vars.Item1;
|
|
||||||
|
static List< string > AddGroupTypes( ulong flags, ulong bound, List< string > groupType )
|
||||||
static List<string> AddGroupTypes(ulong flags, ulong bound, List<string> groupType)
|
{
|
||||||
{
|
List< string > ret = null;
|
||||||
List<string> ret = null;
|
if( flags != uint.MaxValue )
|
||||||
if (flags != uint.MaxValue)
|
{
|
||||||
{
|
ret = new();
|
||||||
ret = new();
|
for( var i = 0; i < groupType.Count; ++i )
|
||||||
for (var i = 0; i < groupType.Count; ++i)
|
{
|
||||||
{
|
var flag = 1u << i;
|
||||||
var flag = 1u << i;
|
if( ( flags & flag ) == flag )
|
||||||
if ((flags & flag) == flag)
|
ret.Add( groupType[ i ] );
|
||||||
ret.Add(groupType[i]);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tops and Bottoms are uint.
|
// Tops and Bottoms are uint.
|
||||||
TopTypes = AddGroupTypes(vars.Item2, uint.MaxValue, info.TopTypes);
|
TopTypes = AddGroupTypes( vars.Item2, uint.MaxValue, info.TopTypes );
|
||||||
BottomTypes = AddGroupTypes(vars.Item3, uint.MaxValue, info.BottomTypes);
|
BottomTypes = AddGroupTypes( vars.Item3, uint.MaxValue, info.BottomTypes );
|
||||||
// Exclusions are the other way around and ulong.
|
// Exclusions are the other way around and ulong.
|
||||||
GroupExclusions = AddGroupTypes(~vars.Item4, 0, info.OtherGroups);
|
GroupExclusions = AddGroupTypes( ~vars.Item4, 0, info.OtherGroups );
|
||||||
}
|
}
|
||||||
|
|
||||||
public (string, uint, uint, ulong) ToTuple(GroupInformation info)
|
public (string, uint, uint, ulong) ToTuple( GroupInformation info )
|
||||||
{
|
{
|
||||||
static ulong TypesToFlags(List<string> ownTypes, List<string> globalTypes)
|
static ulong TypesToFlags( List< string > ownTypes, List< string > globalTypes )
|
||||||
{
|
{
|
||||||
if (ownTypes == null)
|
if( ownTypes == null )
|
||||||
return ulong.MaxValue;
|
return ulong.MaxValue;
|
||||||
|
|
||||||
ulong flags = 0;
|
ulong flags = 0;
|
||||||
foreach (var x in ownTypes)
|
foreach( var x in ownTypes )
|
||||||
{
|
{
|
||||||
var index = globalTypes.IndexOf(x);
|
var index = globalTypes.IndexOf( x );
|
||||||
if (index >= 0)
|
if( index >= 0 )
|
||||||
flags |= (1u << index);
|
flags |= ( 1u << index );
|
||||||
}
|
}
|
||||||
return flags;
|
|
||||||
}
|
return flags;
|
||||||
var tops = (uint) TypesToFlags(TopTypes, info.TopTypes);
|
}
|
||||||
var bottoms = (uint) TypesToFlags(BottomTypes, info.BottomTypes);
|
|
||||||
// Exclusions are the other way around.
|
var tops = ( uint )TypesToFlags( TopTypes, info.TopTypes );
|
||||||
var groupEx = (GroupExclusions == null) ? ulong.MaxValue : ~TypesToFlags(GroupExclusions, info.OtherGroups);
|
var bottoms = ( uint )TypesToFlags( BottomTypes, info.BottomTypes );
|
||||||
return (GamePath, tops, bottoms, groupEx);
|
// Exclusions are the other way around.
|
||||||
}
|
var groupEx = ( GroupExclusions == null ) ? ulong.MaxValue : ~TypesToFlags( GroupExclusions, info.OtherGroups );
|
||||||
|
return ( GamePath, tops, bottoms, groupEx );
|
||||||
public string GamePath { get; set; }
|
}
|
||||||
public List<string> TopTypes { get; set; } = null;
|
|
||||||
public List<string> BottomTypes { get; set; } = null;
|
public string GamePath { get; set; }
|
||||||
public List<string> GroupExclusions { get; set; } = null;
|
public List< string > TopTypes { get; set; }
|
||||||
|
public List< string > BottomTypes { get; set; }
|
||||||
// Customize (De)-Serialization to ignore nulls.
|
public List< string > GroupExclusions { get; set; }
|
||||||
public GroupDescription(SerializationInfo info, StreamingContext context)
|
|
||||||
{
|
// Customize (De)-Serialization to ignore nulls.
|
||||||
List<string> readListOrNull(string name)
|
public GroupDescription( SerializationInfo info, StreamingContext context )
|
||||||
{
|
{
|
||||||
try
|
List< string > readListOrNull( string name )
|
||||||
{
|
{
|
||||||
var ret = (List<string>) info.GetValue(name, typeof(List<string>));
|
try
|
||||||
if (ret == null || ret.Count == 0)
|
{
|
||||||
return null;
|
var ret = ( List< string > )info.GetValue( name, typeof( List< string > ) );
|
||||||
return ret;
|
if( ret == null || ret.Count == 0 )
|
||||||
}
|
return null;
|
||||||
catch (Exception) { return null; }
|
return ret;
|
||||||
}
|
}
|
||||||
GamePath = info.GetString("GamePath");
|
catch( Exception )
|
||||||
TopTypes = readListOrNull("TopTypes");
|
{
|
||||||
BottomTypes = readListOrNull("BottomTypes");
|
return null;
|
||||||
GroupExclusions = readListOrNull("GroupExclusions");
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
|
GamePath = info.GetString( "GamePath" );
|
||||||
{
|
TopTypes = readListOrNull( "TopTypes" );
|
||||||
info.AddValue( "GamePath", GamePath );
|
BottomTypes = readListOrNull( "BottomTypes" );
|
||||||
if (TopTypes != null) info.AddValue("TopTypes", TopTypes);
|
GroupExclusions = readListOrNull( "GroupExclusions" );
|
||||||
if (BottomTypes != null) info.AddValue("BottomTypes", BottomTypes);
|
}
|
||||||
if (GroupExclusions != null) info.AddValue("GroupExclusions", GroupExclusions);
|
|
||||||
}
|
public virtual void GetObjectData( SerializationInfo info, StreamingContext context )
|
||||||
}
|
{
|
||||||
|
info.AddValue( "GamePath", GamePath );
|
||||||
public List<string> TopTypes { get; set; } = new();
|
if( TopTypes != null ) info.AddValue( "TopTypes", TopTypes );
|
||||||
public List<string> BottomTypes { get; set; } = new();
|
if( BottomTypes != null ) info.AddValue( "BottomTypes", BottomTypes );
|
||||||
public List<string> OtherGroups { get; set; } = new();
|
if( GroupExclusions != null ) info.AddValue( "GroupExclusions", GroupExclusions );
|
||||||
|
}
|
||||||
public void AddFileToOtherGroups(string optionName, string fileName, string gamePath)
|
}
|
||||||
{
|
|
||||||
var idx = OtherGroups.IndexOf(optionName);
|
public List< string > TopTypes { get; set; } = new();
|
||||||
if (idx < 0)
|
public List< string > BottomTypes { get; set; } = new();
|
||||||
{
|
public List< string > OtherGroups { get; set; } = new();
|
||||||
idx = OtherGroups.Count;
|
|
||||||
OtherGroups.Add(optionName);
|
public void AddFileToOtherGroups( string optionName, string fileName, string gamePath )
|
||||||
}
|
{
|
||||||
|
var idx = OtherGroups.IndexOf( optionName );
|
||||||
(string, uint, uint, ulong) tuple = (gamePath, uint.MaxValue, uint.MaxValue, (1ul << idx));
|
if( idx < 0 )
|
||||||
|
{
|
||||||
if (!FileToGameAndGroup.TryGetValue(fileName, out var tuple2))
|
idx = OtherGroups.Count;
|
||||||
{
|
OtherGroups.Add( optionName );
|
||||||
FileToGameAndGroup.Add(fileName, tuple);
|
}
|
||||||
}
|
|
||||||
else
|
(string, uint, uint, ulong) tuple = ( gamePath, uint.MaxValue, uint.MaxValue, ( 1ul << idx ) );
|
||||||
{
|
|
||||||
tuple2.Item1 = tuple.Item1;
|
if( !FileToGameAndGroup.TryGetValue( fileName, out var tuple2 ) )
|
||||||
tuple2.Item4 |= tuple.Item4;
|
{
|
||||||
}
|
FileToGameAndGroup.Add( fileName, tuple );
|
||||||
}
|
}
|
||||||
|
else
|
||||||
public Dictionary<string, (string, uint, uint, ulong)> FileToGameAndGroup { get; set; } = new();
|
{
|
||||||
|
tuple2.Item1 = tuple.Item1;
|
||||||
public GroupInformation(){ }
|
tuple2.Item4 |= tuple.Item4;
|
||||||
|
}
|
||||||
public GroupInformation(SerializationInfo info, StreamingContext context)
|
}
|
||||||
{
|
|
||||||
try { TopTypes = (List<string>) info.GetValue( "TopTypes", TopTypes.GetType() ); } catch(Exception){ }
|
public Dictionary< string, (string, uint, uint, ulong) > FileToGameAndGroup { get; set; } = new();
|
||||||
try { BottomTypes = (List<string>) info.GetValue( "BottomTypes", BottomTypes.GetType() ); } catch(Exception){ }
|
|
||||||
try { OtherGroups = (List<string>) info.GetValue( "Groups", OtherGroups.GetType() ); } catch(Exception){ }
|
public GroupInformation()
|
||||||
try
|
{
|
||||||
{
|
}
|
||||||
Dictionary<string, GroupDescription> dict = new();
|
|
||||||
dict = (Dictionary<string, GroupDescription>) info.GetValue( "FileToGameAndGroups", dict.GetType());
|
public GroupInformation( SerializationInfo info, StreamingContext context )
|
||||||
foreach (var pair in dict)
|
{
|
||||||
FileToGameAndGroup.Add(pair.Key, pair.Value.ToTuple(this));
|
try
|
||||||
} catch (Exception){ }
|
{
|
||||||
}
|
TopTypes = ( List< string > )info.GetValue( "TopTypes", TopTypes.GetType() );
|
||||||
|
}
|
||||||
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
|
catch( Exception )
|
||||||
{
|
{
|
||||||
if ((TopTypes?.Count ?? 0) > 0) info.AddValue("TopTypes", TopTypes);
|
}
|
||||||
if ((BottomTypes?.Count ?? 0) > 0) info.AddValue("BottomTypes", BottomTypes);
|
|
||||||
if ((OtherGroups?.Count ?? 0) > 0) info.AddValue("Groups", OtherGroups);
|
try
|
||||||
if ((FileToGameAndGroup?.Count ?? 0) > 0)
|
{
|
||||||
{
|
BottomTypes = ( List< string > )info.GetValue( "BottomTypes", BottomTypes.GetType() );
|
||||||
var dict = FileToGameAndGroup.ToDictionary( pair => pair.Key, pair => new GroupDescription( this, pair.Value ) );
|
}
|
||||||
info.AddValue("FileToGameAndGroups", dict);
|
catch( Exception )
|
||||||
}
|
{
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OtherGroups = ( List< string > )info.GetValue( "Groups", OtherGroups.GetType() );
|
||||||
|
}
|
||||||
|
catch( Exception )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Dictionary< string, GroupDescription > dict = new();
|
||||||
|
dict = ( Dictionary< string, GroupDescription > )info.GetValue( "FileToGameAndGroups", dict.GetType() );
|
||||||
|
foreach( var pair in dict )
|
||||||
|
FileToGameAndGroup.Add( pair.Key, pair.Value.ToTuple( this ) );
|
||||||
|
}
|
||||||
|
catch( Exception )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void GetObjectData( SerializationInfo info, StreamingContext context )
|
||||||
|
{
|
||||||
|
if( ( TopTypes?.Count ?? 0 ) > 0 ) info.AddValue( "TopTypes", TopTypes );
|
||||||
|
if( ( BottomTypes?.Count ?? 0 ) > 0 ) info.AddValue( "BottomTypes", BottomTypes );
|
||||||
|
if( ( OtherGroups?.Count ?? 0 ) > 0 ) info.AddValue( "Groups", OtherGroups );
|
||||||
|
if( ( FileToGameAndGroup?.Count ?? 0 ) > 0 )
|
||||||
|
{
|
||||||
|
var dict = FileToGameAndGroup.ToDictionary( pair => pair.Key, pair => new GroupDescription( this, pair.Value ) );
|
||||||
|
info.AddValue( "FileToGameAndGroups", dict );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,18 +4,19 @@ namespace Penumbra.Models
|
||||||
{
|
{
|
||||||
public class ModMeta
|
public class ModMeta
|
||||||
{
|
{
|
||||||
|
public uint FileVersion { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Author { get; set; }
|
public string Author { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
|
||||||
public string Version { get; set; }
|
public string Version { get; set; }
|
||||||
|
|
||||||
public string Website { get; set; }
|
public string Website { get; set; }
|
||||||
|
|
||||||
public List<string> ChangedItems { get; set; } = new();
|
public List< string > ChangedItems { get; set; } = new();
|
||||||
|
|
||||||
public Dictionary< string, string > FileSwaps { get; } = new();
|
public Dictionary< string, string > FileSwaps { get; } = new();
|
||||||
|
|
||||||
public GroupInformation Groups { get; set; } = new();
|
public GroupInformation Groups { get; set; } = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -21,7 +21,7 @@ namespace Penumbra.Mods
|
||||||
_basePath = basePath;
|
_basePath = basePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Load()
|
public void Load( bool invertOrder = false )
|
||||||
{
|
{
|
||||||
// find the collection json
|
// find the collection json
|
||||||
var collectionPath = Path.Combine( _basePath.FullName, "collection.json" );
|
var collectionPath = Path.Combine( _basePath.FullName, "collection.json" );
|
||||||
|
|
@ -94,7 +94,7 @@ namespace Penumbra.Mods
|
||||||
}
|
}
|
||||||
|
|
||||||
// reorder the resourcemods list so we can just directly iterate
|
// reorder the resourcemods list so we can just directly iterate
|
||||||
EnabledMods = GetOrderedAndEnabledModList().ToArray();
|
EnabledMods = GetOrderedAndEnabledModList( invertOrder ).ToArray();
|
||||||
|
|
||||||
// write the collection metadata back to disk
|
// write the collection metadata back to disk
|
||||||
Save();
|
Save();
|
||||||
|
|
@ -181,22 +181,28 @@ namespace Penumbra.Mods
|
||||||
return AddModSettings( mod );
|
return AddModSettings( mod );
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ModInfo> GetOrderedAndEnabledModSettings()
|
public IEnumerable<ModInfo> GetOrderedAndEnabledModSettings( bool invertOrder = false )
|
||||||
{
|
{
|
||||||
return ModSettings
|
var query = ModSettings
|
||||||
.Where( x => x.Enabled )
|
.Where( x => x.Enabled );
|
||||||
.OrderBy( x => x.Priority );
|
|
||||||
}
|
if( !invertOrder )
|
||||||
|
{
|
||||||
public IEnumerable<ResourceMod> GetOrderedAndEnabledModList()
|
return query.OrderBy( x => x.Priority );
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.OrderByDescending( x => x.Priority );
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ResourceMod> GetOrderedAndEnabledModList( bool invertOrder = false )
|
||||||
{
|
{
|
||||||
return GetOrderedAndEnabledModSettings()
|
return GetOrderedAndEnabledModSettings( invertOrder )
|
||||||
.Select( x => x.Mod );
|
.Select( x => x.Mod );
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<(ResourceMod, ModInfo)> GetOrderedAndEnabledModListWithSettings()
|
public IEnumerable<(ResourceMod, ModInfo)> GetOrderedAndEnabledModListWithSettings( bool invertOrder = false )
|
||||||
{
|
{
|
||||||
return GetOrderedAndEnabledModSettings()
|
return GetOrderedAndEnabledModSettings( invertOrder )
|
||||||
.Select( x => (x.Mod, x) );
|
.Select( x => (x.Mod, x) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Penumbra.Models;
|
using Penumbra.Models;
|
||||||
|
|
||||||
namespace Penumbra.Mods
|
namespace Penumbra.Mods
|
||||||
{
|
{
|
||||||
public class ModManager : IDisposable
|
public class ModManager : IDisposable
|
||||||
{
|
{
|
||||||
private readonly Plugin _plugin;
|
private readonly Plugin _plugin;
|
||||||
public readonly Dictionary< string, FileInfo > ResolvedFiles = new();
|
public readonly Dictionary< string, FileInfo > ResolvedFiles = new();
|
||||||
public readonly Dictionary< string, string > SwappedFiles = new();
|
public readonly Dictionary< string, string > SwappedFiles = new();
|
||||||
|
|
||||||
public ModCollection Mods { get; set; }
|
public ModCollection Mods { get; set; }
|
||||||
|
|
@ -98,45 +98,45 @@ namespace Penumbra.Mods
|
||||||
Mods.Save();
|
Mods.Save();
|
||||||
|
|
||||||
CalculateEffectiveFileList();
|
CalculateEffectiveFileList();
|
||||||
|
|
||||||
// Needed to reload body textures with mods
|
// Needed to reload body textures with mods
|
||||||
_plugin.GameUtils.ReloadPlayerResources();
|
_plugin.GameUtils.ReloadPlayerResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CalculateEffectiveFileList()
|
public void CalculateEffectiveFileList()
|
||||||
{
|
{
|
||||||
ResolvedFiles.Clear();
|
ResolvedFiles.Clear();
|
||||||
SwappedFiles.Clear();
|
SwappedFiles.Clear();
|
||||||
|
|
||||||
var registeredFiles = new Dictionary< string, string >();
|
var registeredFiles = new Dictionary< string, string >();
|
||||||
|
|
||||||
foreach( var (mod, settings) in Mods.GetOrderedAndEnabledModListWithSettings() )
|
foreach( var (mod, settings) in Mods.GetOrderedAndEnabledModListWithSettings( _plugin.Configuration.InvertModListOrder ) )
|
||||||
{
|
{
|
||||||
mod.FileConflicts?.Clear();
|
mod.FileConflicts?.Clear();
|
||||||
|
|
||||||
// fixup path
|
// fixup path
|
||||||
var baseDir = mod.ModBasePath.FullName;
|
var baseDir = mod.ModBasePath.FullName;
|
||||||
|
|
||||||
foreach( var file in mod.ModFiles )
|
foreach( var file in mod.ModFiles )
|
||||||
{
|
{
|
||||||
var relativeFilePath = file.FullName.Substring( baseDir.Length ).TrimStart( '\\' );
|
var relativeFilePath = file.FullName.Substring( baseDir.Length ).TrimStart( '\\' );
|
||||||
|
|
||||||
string gamePath;
|
string gamePath;
|
||||||
bool addFile = true;
|
bool addFile = true;
|
||||||
(string, uint, uint, ulong) tuple;
|
(string, uint, uint, ulong) tuple;
|
||||||
if (mod.Meta.Groups.FileToGameAndGroup.TryGetValue(relativeFilePath, out tuple))
|
if( mod.Meta.Groups.FileToGameAndGroup.TryGetValue( relativeFilePath, out tuple ) )
|
||||||
{
|
{
|
||||||
gamePath = tuple.Item1;
|
gamePath = tuple.Item1;
|
||||||
var (_, tops, bottoms, excludes) = tuple;
|
var (_, tops, bottoms, excludes) = tuple;
|
||||||
var validTop = ((1u << settings.CurrentTop) & tops ) != 0;
|
var validTop = ( ( 1u << settings.CurrentTop ) & tops ) != 0;
|
||||||
var validBottom = ((1u << settings.CurrentBottom) & bottoms ) != 0;
|
var validBottom = ( ( 1u << settings.CurrentBottom ) & bottoms ) != 0;
|
||||||
var validGroup = ((1ul << settings.CurrentGroup) & excludes) != 0;
|
var validGroup = ( ( 1ul << settings.CurrentGroup ) & excludes ) != 0;
|
||||||
addFile = validTop && validBottom && validGroup;
|
addFile = validTop && validBottom && validGroup;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
gamePath = relativeFilePath.Replace( '\\', '/' );
|
gamePath = relativeFilePath.Replace( '\\', '/' );
|
||||||
|
|
||||||
if ( addFile )
|
if( addFile )
|
||||||
{
|
{
|
||||||
if( !ResolvedFiles.ContainsKey( gamePath ) )
|
if( !ResolvedFiles.ContainsKey( gamePath ) )
|
||||||
{
|
{
|
||||||
|
|
@ -146,10 +146,10 @@ namespace Penumbra.Mods
|
||||||
else if( registeredFiles.TryGetValue( gamePath, out var modName ) )
|
else if( registeredFiles.TryGetValue( gamePath, out var modName ) )
|
||||||
{
|
{
|
||||||
mod.AddConflict( modName, gamePath );
|
mod.AddConflict( modName, gamePath );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach( var swap in mod.Meta.FileSwaps )
|
foreach( var swap in mod.Meta.FileSwaps )
|
||||||
{
|
{
|
||||||
// just assume people put not fucked paths in here lol
|
// just assume people put not fucked paths in here lol
|
||||||
|
|
@ -160,8 +160,8 @@ namespace Penumbra.Mods
|
||||||
}
|
}
|
||||||
else if( registeredFiles.TryGetValue( swap.Key, out var modName ) )
|
else if( registeredFiles.TryGetValue( swap.Key, out var modName ) )
|
||||||
{
|
{
|
||||||
mod.AddConflict( modName, swap.Key );
|
mod.AddConflict( modName, swap.Key );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -202,10 +202,10 @@ namespace Penumbra.Mods
|
||||||
public string ResolveSwappedOrReplacementFilePath( string gameResourcePath )
|
public string ResolveSwappedOrReplacementFilePath( string gameResourcePath )
|
||||||
{
|
{
|
||||||
gameResourcePath = gameResourcePath.ToLowerInvariant();
|
gameResourcePath = gameResourcePath.ToLowerInvariant();
|
||||||
|
|
||||||
return GetCandidateForGameFile( gameResourcePath )?.FullName ?? GetSwappedFilePath( gameResourcePath );
|
return GetCandidateForGameFile( gameResourcePath )?.FullName ?? GetSwappedFilePath( gameResourcePath );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,10 @@ namespace Penumbra.Mods
|
||||||
ModFiles.Add( file );
|
ModFiles.Add( file );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only add if not in a sub-folder, otherwise it was already added.
|
// Only add if not in a sub-folder, otherwise it was already added.
|
||||||
foreach( var pair in Meta.Groups.FileToGameAndGroup )
|
foreach( var pair in Meta.Groups.FileToGameAndGroup )
|
||||||
if (pair.Key.IndexOfAny(new char[]{'/', '\\'}) < 0)
|
if (pair.Key.IndexOfAny(new[]{'/', '\\'}) < 0)
|
||||||
ModFiles.Add( new FileInfo(Path.Combine(ModBasePath.FullName, pair.Key)) );
|
ModFiles.Add( new FileInfo(Path.Combine(ModBasePath.FullName, pair.Key)) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net472</TargetFramework>
|
<TargetFramework>net472</TargetFramework>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<AssemblyTitle>Penumbra</AssemblyTitle>
|
<AssemblyTitle>Penumbra</AssemblyTitle>
|
||||||
<Company>absolute gangstas</Company>
|
<Company>absolute gangstas</Company>
|
||||||
<Product>Penumbra</Product>
|
<Product>Penumbra</Product>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
using Dalamud.Game.Command;
|
using Dalamud.Game.Command;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using EmbedIO;
|
using EmbedIO;
|
||||||
using EmbedIO.Actions;
|
|
||||||
using EmbedIO.WebApi;
|
using EmbedIO.WebApi;
|
||||||
using Penumbra.API;
|
using Penumbra.API;
|
||||||
using Penumbra.Game;
|
using Penumbra.Game;
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ using System.Numerics;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Penumbra.Importer;
|
using Penumbra.Importer;
|
||||||
using Penumbra.Models;
|
using Penumbra.Models;
|
||||||
|
|
@ -18,8 +18,8 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
private readonly Plugin _plugin;
|
private readonly Plugin _plugin;
|
||||||
|
|
||||||
public bool Visible = false;
|
public bool Visible;
|
||||||
public bool ShowDebugBar = false;
|
public bool ShowDebugBar;
|
||||||
|
|
||||||
private static readonly Vector2 AutoFillSize = new Vector2( -1, -1 );
|
private static readonly Vector2 AutoFillSize = new Vector2( -1, -1 );
|
||||||
private static readonly Vector2 ModListSize = new Vector2( 200, -1 );
|
private static readonly Vector2 ModListSize = new Vector2( 200, -1 );
|
||||||
|
|
@ -33,7 +33,7 @@ namespace Penumbra.UI
|
||||||
private int? _selectedModDeleteIndex;
|
private int? _selectedModDeleteIndex;
|
||||||
private ModInfo _selectedMod;
|
private ModInfo _selectedMod;
|
||||||
|
|
||||||
public bool IsImportRunning = false;
|
private bool _isImportRunning;
|
||||||
private TexToolsImport _texToolsImport = null!;
|
private TexToolsImport _texToolsImport = null!;
|
||||||
|
|
||||||
public SettingsInterface( Plugin plugin )
|
public SettingsInterface( Plugin plugin )
|
||||||
|
|
@ -129,7 +129,7 @@ namespace Penumbra.UI
|
||||||
DrawImportTab();
|
DrawImportTab();
|
||||||
|
|
||||||
|
|
||||||
if( !IsImportRunning )
|
if( !_isImportRunning )
|
||||||
{
|
{
|
||||||
DrawModBrowser();
|
DrawModBrowser();
|
||||||
|
|
||||||
|
|
@ -156,11 +156,11 @@ namespace Penumbra.UI
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !IsImportRunning )
|
if( !_isImportRunning )
|
||||||
{
|
{
|
||||||
if( ImGui.Button( "Import TexTools Modpacks" ) )
|
if( ImGui.Button( "Import TexTools Modpacks" ) )
|
||||||
{
|
{
|
||||||
IsImportRunning = true;
|
_isImportRunning = true;
|
||||||
|
|
||||||
Task.Run( async () =>
|
Task.Run( async () =>
|
||||||
{
|
{
|
||||||
|
|
@ -197,7 +197,7 @@ namespace Penumbra.UI
|
||||||
ReloadMods();
|
ReloadMods();
|
||||||
}
|
}
|
||||||
|
|
||||||
IsImportRunning = false;
|
_isImportRunning = false;
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -267,9 +267,9 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
if( ImGui.Button( "Rediscover Mods" ) )
|
if( ImGui.Button( "Rediscover Mods" ) )
|
||||||
{
|
{
|
||||||
ReloadMods();
|
ReloadMods();
|
||||||
_selectedModIndex = 0;
|
_selectedModIndex = 0;
|
||||||
_selectedMod = null;
|
_selectedMod = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
@ -279,7 +279,18 @@ namespace Penumbra.UI
|
||||||
Process.Start( _plugin.Configuration.CurrentCollection );
|
Process.Start( _plugin.Configuration.CurrentCollection );
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SetCursorPosY( ImGui.GetCursorPosY() + 15 );
|
ImGui.SetCursorPosY( ImGui.GetCursorPosY() + 20 );
|
||||||
|
|
||||||
|
var invertOrder = _plugin.Configuration.InvertModListOrder;
|
||||||
|
if( ImGui.Checkbox( "Invert mod load order (mods are loaded bottom up)", ref invertOrder ) )
|
||||||
|
{
|
||||||
|
_plugin.Configuration.InvertModListOrder = invertOrder;
|
||||||
|
dirty = true;
|
||||||
|
|
||||||
|
ReloadMods();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SetCursorPosY( ImGui.GetCursorPosY() + 20 );
|
||||||
|
|
||||||
var showAdvanced = _plugin.Configuration.ShowAdvanced;
|
var showAdvanced = _plugin.Configuration.ShowAdvanced;
|
||||||
if( ImGui.Checkbox( "Show Advanced Settings", ref showAdvanced ) )
|
if( ImGui.Checkbox( "Show Advanced Settings", ref showAdvanced ) )
|
||||||
|
|
@ -405,7 +416,13 @@ namespace Penumbra.UI
|
||||||
ImGui.PopFont();
|
ImGui.PopFont();
|
||||||
|
|
||||||
if( ImGui.IsItemHovered() )
|
if( ImGui.IsItemHovered() )
|
||||||
ImGui.SetTooltip( "Move the selected mod up in priority" );
|
{
|
||||||
|
ImGui.SetTooltip(
|
||||||
|
_plugin.Configuration.InvertModListOrder
|
||||||
|
? "Move the selected mod down in priority"
|
||||||
|
: "Move the selected mod up in priority"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.PushFont( UiBuilder.IconFont );
|
ImGui.PushFont( UiBuilder.IconFont );
|
||||||
|
|
||||||
|
|
@ -430,7 +447,13 @@ namespace Penumbra.UI
|
||||||
ImGui.PopFont();
|
ImGui.PopFont();
|
||||||
|
|
||||||
if( ImGui.IsItemHovered() )
|
if( ImGui.IsItemHovered() )
|
||||||
ImGui.SetTooltip( "Move the selected mod down in priority" );
|
{
|
||||||
|
ImGui.SetTooltip(
|
||||||
|
_plugin.Configuration.InvertModListOrder
|
||||||
|
? "Move the selected mod up in priority"
|
||||||
|
: "Move the selected mod down in priority"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.PushFont( UiBuilder.IconFont );
|
ImGui.PushFont( UiBuilder.IconFont );
|
||||||
|
|
||||||
|
|
@ -503,106 +526,109 @@ namespace Penumbra.UI
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndPopup();
|
ImGui.EndPopup();
|
||||||
}
|
|
||||||
|
|
||||||
// Website button with On-Hover address if valid http(s), otherwise text.
|
|
||||||
private void DrawWebsiteText()
|
|
||||||
{
|
|
||||||
if ((_selectedMod.Mod.Meta.Website?.Length ?? 0) > 0)
|
|
||||||
{
|
|
||||||
var validUrl = Uri.TryCreate(_selectedMod.Mod.Meta.Website, UriKind.Absolute, out Uri uriResult)
|
|
||||||
&& (uriResult.Scheme == Uri.UriSchemeHttps ||uriResult.Scheme == Uri.UriSchemeHttp);
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (validUrl)
|
|
||||||
{
|
|
||||||
if (ImGui.SmallButton("Open Website"))
|
|
||||||
{
|
|
||||||
Process.Start( _selectedMod.Mod.Meta.Website );
|
|
||||||
}
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
{
|
|
||||||
ImGui.BeginTooltip();
|
|
||||||
ImGui.Text( _selectedMod.Mod.Meta.Website );
|
|
||||||
ImGui.EndTooltip();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.TextColored( new Vector4( 1f, 1f, 1f, 0.66f ), "from" );
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.Text(_selectedMod.Mod.Meta.Website);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Mod-Handling buttons.
|
// Website button with On-Hover address if valid http(s), otherwise text.
|
||||||
private void DrawEditButtons()
|
private void DrawWebsiteText()
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
if( ( _selectedMod.Mod.Meta.Website?.Length ?? 0 ) <= 0 )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var validUrl = Uri.TryCreate( _selectedMod.Mod.Meta.Website, UriKind.Absolute, out Uri uriResult )
|
||||||
|
&& ( uriResult.Scheme == Uri.UriSchemeHttps || uriResult.Scheme == Uri.UriSchemeHttp );
|
||||||
|
ImGui.SameLine();
|
||||||
|
if( validUrl )
|
||||||
|
{
|
||||||
|
if( ImGui.SmallButton( "Open Website" ) )
|
||||||
|
{
|
||||||
|
Process.Start( _selectedMod.Mod.Meta.Website );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ImGui.IsItemHovered() )
|
||||||
|
{
|
||||||
|
ImGui.BeginTooltip();
|
||||||
|
ImGui.Text( _selectedMod.Mod.Meta.Website );
|
||||||
|
ImGui.EndTooltip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.TextColored( new Vector4( 1f, 1f, 1f, 0.66f ), "from" );
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.Text( _selectedMod.Mod.Meta.Website );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Mod-Handling buttons.
|
||||||
|
private void DrawEditButtons()
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
if( ImGui.Button( "Open Mod Folder" ) )
|
if( ImGui.Button( "Open Mod Folder" ) )
|
||||||
{
|
{
|
||||||
Process.Start( _selectedMod.Mod.ModBasePath.FullName );
|
Process.Start( _selectedMod.Mod.ModBasePath.FullName );
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if( ImGui.Button( "Edit JSON" ) )
|
if( ImGui.Button( "Edit JSON" ) )
|
||||||
{
|
{
|
||||||
var metaPath = Path.Combine( _selectedMod.Mod.ModBasePath.FullName, "meta.json");
|
var metaPath = Path.Combine( _selectedMod.Mod.ModBasePath.FullName, "meta.json" );
|
||||||
File.WriteAllText( metaPath, JsonConvert.SerializeObject( _selectedMod.Mod.Meta, Formatting.Indented ) );
|
File.WriteAllText( metaPath, JsonConvert.SerializeObject( _selectedMod.Mod.Meta, Formatting.Indented ) );
|
||||||
Process.Start( metaPath );
|
Process.Start( metaPath );
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if( ImGui.Button( "Reload JSON" ) )
|
if( ImGui.Button( "Reload JSON" ) )
|
||||||
{
|
{
|
||||||
ReloadMods();
|
ReloadMods();
|
||||||
|
|
||||||
// May select a different mod than before if mods were added or deleted, but will not crash.
|
// May select a different mod than before if mods were added or deleted, but will not crash.
|
||||||
if (_selectedModIndex < _plugin.ModManager.Mods.ModSettings.Count)
|
if( _selectedModIndex < _plugin.ModManager.Mods.ModSettings.Count )
|
||||||
{
|
{
|
||||||
_selectedMod = _plugin.ModManager.Mods.ModSettings[_selectedModIndex];
|
_selectedMod = _plugin.ModManager.Mods.ModSettings[ _selectedModIndex ];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_selectedModIndex = 0;
|
_selectedModIndex = 0;
|
||||||
_selectedMod = null;
|
_selectedMod = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGroupSelectors()
|
private void DrawGroupSelectors()
|
||||||
{
|
{
|
||||||
var hasTopTypes = (_selectedMod.Mod.Meta.Groups.TopTypes?.Count ?? 0) > 1;
|
var hasTopTypes = ( _selectedMod.Mod.Meta.Groups.TopTypes?.Count ?? 0 ) > 1;
|
||||||
var hasBottomTypes = (_selectedMod.Mod.Meta.Groups.BottomTypes?.Count ?? 0) > 1;
|
var hasBottomTypes = ( _selectedMod.Mod.Meta.Groups.BottomTypes?.Count ?? 0 ) > 1;
|
||||||
var hasGroups = (_selectedMod.Mod.Meta.Groups.OtherGroups?.Count ?? 0) > 1;
|
var hasGroups = ( _selectedMod.Mod.Meta.Groups.OtherGroups?.Count ?? 0 ) > 1;
|
||||||
var numSelectors = (hasTopTypes ? 1 : 0) + (hasBottomTypes ? 1 : 0) + (hasGroups ? 1 : 0);
|
var numSelectors = ( hasTopTypes ? 1 : 0 ) + ( hasBottomTypes ? 1 : 0 ) + ( hasGroups ? 1 : 0 );
|
||||||
var selectorWidth = (ImGui.GetWindowWidth()
|
var selectorWidth = ( ImGui.GetWindowWidth()
|
||||||
- (hasTopTypes ? ImGui.CalcTextSize("Top ").X : 0)
|
- ( hasTopTypes ? ImGui.CalcTextSize( "Top " ).X : 0 )
|
||||||
- (hasBottomTypes ? ImGui.CalcTextSize("Bottom ").X : 0)
|
- ( hasBottomTypes ? ImGui.CalcTextSize( "Bottom " ).X : 0 )
|
||||||
- (hasGroups ? ImGui.CalcTextSize("Group ").X : 0)) / numSelectors;
|
- ( hasGroups ? ImGui.CalcTextSize( "Group " ).X : 0 ) ) / numSelectors;
|
||||||
|
|
||||||
void DrawSelector(string label, string propertyName, System.Collections.Generic.List<string> list, bool sameLine)
|
void DrawSelector( string label, string propertyName, System.Collections.Generic.List< string > list, bool sameLine )
|
||||||
{
|
{
|
||||||
var current = (int) _selectedMod.GetType().GetProperty(propertyName).GetValue(_selectedMod);
|
var current = ( int )_selectedMod.GetType().GetProperty( propertyName ).GetValue( _selectedMod );
|
||||||
ImGui.SetNextItemWidth( selectorWidth );
|
ImGui.SetNextItemWidth( selectorWidth );
|
||||||
if (sameLine) ImGui.SameLine();
|
if( sameLine ) ImGui.SameLine();
|
||||||
if ( ImGui.Combo(label, ref current, list.ToArray(), list.Count()) )
|
if( ImGui.Combo( label, ref current, list.ToArray(), list.Count() ) )
|
||||||
{
|
{
|
||||||
_selectedMod.GetType().GetProperty(propertyName).SetValue(_selectedMod, current);
|
_selectedMod.GetType().GetProperty( propertyName ).SetValue( _selectedMod, current );
|
||||||
_plugin.ModManager.Mods.Save();
|
_plugin.ModManager.Mods.Save();
|
||||||
_plugin.ModManager.CalculateEffectiveFileList();
|
_plugin.ModManager.CalculateEffectiveFileList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( hasTopTypes )
|
if( hasTopTypes )
|
||||||
DrawSelector("Top", "CurrentTop", _selectedMod.Mod.Meta.Groups.TopTypes, false);
|
DrawSelector( "Top", "CurrentTop", _selectedMod.Mod.Meta.Groups.TopTypes, false );
|
||||||
|
|
||||||
if ( hasBottomTypes )
|
if( hasBottomTypes )
|
||||||
DrawSelector("Bottom", "CurrentBottom", _selectedMod.Mod.Meta.Groups.BottomTypes, hasTopTypes);
|
DrawSelector( "Bottom", "CurrentBottom", _selectedMod.Mod.Meta.Groups.BottomTypes, hasTopTypes );
|
||||||
|
|
||||||
if ( hasGroups )
|
if( hasGroups )
|
||||||
DrawSelector("Group", "CurrentGroup", _selectedMod.Mod.Meta.Groups.OtherGroups, numSelectors > 1);
|
DrawSelector( "Group", "CurrentGroup", _selectedMod.Mod.Meta.Groups.OtherGroups, numSelectors > 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawInstalledMods()
|
void DrawInstalledMods()
|
||||||
{
|
{
|
||||||
|
|
@ -634,24 +660,24 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
ImGui.BeginChild( "selectedModInfo", AutoFillSize, true );
|
ImGui.BeginChild( "selectedModInfo", AutoFillSize, true );
|
||||||
|
|
||||||
ImGui.Text( _selectedMod.Mod.Meta.Name );
|
ImGui.Text( _selectedMod.Mod.Meta.Name );
|
||||||
|
|
||||||
// (Version ...) or nothing.
|
// (Version ...) or nothing.
|
||||||
if ((_selectedMod.Mod.Meta.Version?.Length ?? 0) > 0)
|
if( ( _selectedMod.Mod.Meta.Version?.Length ?? 0 ) > 0 )
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.Text($"(Version {_selectedMod.Mod.Meta.Version})" );
|
ImGui.Text( $"(Version {_selectedMod.Mod.Meta.Version})" );
|
||||||
}
|
}
|
||||||
|
|
||||||
// by Author or Unknown.
|
// by Author or Unknown.
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.TextColored( new Vector4( 1f, 1f, 1f, 0.66f ), "by" );
|
ImGui.TextColored( new Vector4( 1f, 1f, 1f, 0.66f ), "by" );
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if ((_selectedMod.Mod.Meta.Author?.Length ?? 0) > 0 )
|
if( ( _selectedMod.Mod.Meta.Author?.Length ?? 0 ) > 0 )
|
||||||
ImGui.Text( _selectedMod.Mod.Meta.Author );
|
ImGui.Text( _selectedMod.Mod.Meta.Author );
|
||||||
else
|
else
|
||||||
ImGui.Text( "Unknown" );
|
ImGui.Text( "Unknown" );
|
||||||
|
|
||||||
DrawWebsiteText();
|
DrawWebsiteText();
|
||||||
|
|
||||||
ImGui.SetCursorPosY( ImGui.GetCursorPosY() + 10 );
|
ImGui.SetCursorPosY( ImGui.GetCursorPosY() + 10 );
|
||||||
|
|
@ -662,27 +688,32 @@ namespace Penumbra.UI
|
||||||
_selectedMod.Enabled = enabled;
|
_selectedMod.Enabled = enabled;
|
||||||
_plugin.ModManager.Mods.Save();
|
_plugin.ModManager.Mods.Save();
|
||||||
_plugin.ModManager.CalculateEffectiveFileList();
|
_plugin.ModManager.CalculateEffectiveFileList();
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawEditButtons();
|
DrawEditButtons();
|
||||||
|
|
||||||
DrawGroupSelectors();
|
DrawGroupSelectors();
|
||||||
|
|
||||||
ImGui.TextWrapped( _selectedMod.Mod.Meta.Description ?? "" );
|
ImGui.BeginTabBar( "PenumbraPluginDetails" );
|
||||||
|
|
||||||
ImGui.BeginTabBar( "PenumbraPluginDetails" );
|
if( _selectedMod.Mod.Meta.Description?.Length > 0 && ImGui.BeginTabItem( "About" ) )
|
||||||
if ( (_selectedMod.Mod.Meta.ChangedItems?.Count ?? 0 ) > 0)
|
{
|
||||||
{
|
ImGui.TextWrapped( _selectedMod.Mod.Meta.Description );
|
||||||
if( ImGui.BeginTabItem( "Changed Items" ) )
|
ImGui.EndTabItem();
|
||||||
{
|
}
|
||||||
ImGui.SetNextItemWidth( -1 );
|
|
||||||
if( ImGui.ListBoxHeader( "###", AutoFillSize ) )
|
if( ( _selectedMod.Mod.Meta.ChangedItems?.Count ?? 0 ) > 0 )
|
||||||
foreach(var item in _selectedMod.Mod.Meta.ChangedItems)
|
{
|
||||||
|
if( ImGui.BeginTabItem( "Changed Items" ) )
|
||||||
|
{
|
||||||
|
ImGui.SetNextItemWidth( -1 );
|
||||||
|
if( ImGui.ListBoxHeader( "###", AutoFillSize ) )
|
||||||
|
foreach( var item in _selectedMod.Mod.Meta.ChangedItems )
|
||||||
ImGui.Selectable( item );
|
ImGui.Selectable( item );
|
||||||
ImGui.ListBoxFooter();
|
ImGui.ListBoxFooter();
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ImGui.BeginTabItem( "Files" ) )
|
if( ImGui.BeginTabItem( "Files" ) )
|
||||||
{
|
{
|
||||||
|
|
@ -713,6 +744,7 @@ namespace Penumbra.UI
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( _selectedMod.Mod.FileConflicts.Any() )
|
if( _selectedMod.Mod.FileConflicts.Any() )
|
||||||
{
|
{
|
||||||
if( ImGui.BeginTabItem( "File Conflicts" ) )
|
if( ImGui.BeginTabItem( "File Conflicts" ) )
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
using System;
|
using System.Linq;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Penumbra.Extensions;
|
|
||||||
|
|
||||||
namespace Penumbra.Util
|
namespace Penumbra.Util
|
||||||
{
|
{
|
||||||
|
|
@ -10,14 +8,14 @@ namespace Penumbra.Util
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Crc32
|
public class Crc32
|
||||||
{
|
{
|
||||||
private const uint POLY = 0xedb88320;
|
private const uint Poly = 0xedb88320;
|
||||||
|
|
||||||
private static readonly uint[] CrcArray =
|
private static readonly uint[] CrcArray =
|
||||||
Enumerable.Range( 0, 256 ).Select( i =>
|
Enumerable.Range( 0, 256 ).Select( i =>
|
||||||
{
|
{
|
||||||
var k = ( uint )i;
|
var k = ( uint )i;
|
||||||
for( var j = 0; j < 8; j++ )
|
for( var j = 0; j < 8; j++ )
|
||||||
k = ( k & 1 ) != 0 ? ( k >> 1 ) ^ POLY : k >> 1;
|
k = ( k & 1 ) != 0 ? ( k >> 1 ) ^ Poly : k >> 1;
|
||||||
|
|
||||||
return k;
|
return k;
|
||||||
} ).ToArray();
|
} ).ToArray();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue