Added a whole lot of rudimentary comments, also importing selects the last imported mod if possible.

This commit is contained in:
Ottermandias 2021-06-25 15:40:41 +02:00
parent a19ec226c5
commit d29049ca21
24 changed files with 83 additions and 23 deletions

View file

@ -27,6 +27,7 @@ namespace Penumbra.Game.Enums
Viera, Viera,
} }
// The combined gender-race-npc numerical code as used by the game.
public enum GenderRace : ushort public enum GenderRace : ushort
{ {
Unknown = 0, Unknown = 0,

View file

@ -62,6 +62,7 @@ namespace Penumbra.Hooks
Marshal.GetDelegateForFunctionPointer< UnloadCharacterResourcePrototype >( unloadCharacterResourceAddress ); Marshal.GetDelegateForFunctionPointer< UnloadCharacterResourcePrototype >( unloadCharacterResourceAddress );
} }
// Forces the reload of a specific set of 85 files, notably containing the eqp, eqdp, gmp and est tables, by filename.
public unsafe void ReloadPlayerResources() public unsafe void ReloadPlayerResources()
{ {
ReloadCharacterResources(); ReloadCharacterResources();

View file

@ -3,6 +3,8 @@ using Dalamud.Plugin;
namespace Penumbra.Hooks namespace Penumbra.Hooks
{ {
// Use this to disable streaming of specific soundfiles,
// which will allow replacement of .scd files.
public unsafe class MusicManager public unsafe class MusicManager
{ {
private readonly IntPtr _musicManager; private readonly IntPtr _musicManager;

View file

@ -21,6 +21,8 @@ namespace Penumbra.Importer
private const string TempFileName = "textools-import"; private const string TempFileName = "textools-import";
private readonly string _resolvedTempFilePath; private readonly string _resolvedTempFilePath;
public DirectoryInfo? ExtractedDirectory { get; private set; }
public ImporterState State { get; private set; } public ImporterState State { get; private set; }
public long TotalProgress { get; private set; } public long TotalProgress { get; private set; }
@ -161,14 +163,14 @@ namespace Penumbra.Importer
// Open the mod data file from the modpack as a SqPackStream // Open the mod data file from the modpack as a SqPackStream
using var modData = GetMagicSqPackDeleterStream( extractedModPack, "TTMPD.mpd" ); using var modData = GetMagicSqPackDeleterStream( extractedModPack, "TTMPD.mpd" );
var newModFolder = CreateModFolder( _outDirectory, Path.GetFileNameWithoutExtension( modPackFile.Name ) ); ExtractedDirectory = CreateModFolder( _outDirectory, Path.GetFileNameWithoutExtension( modPackFile.Name ) );
File.WriteAllText( File.WriteAllText(
Path.Combine( newModFolder.FullName, "meta.json" ), Path.Combine( ExtractedDirectory.FullName, "meta.json" ),
JsonConvert.SerializeObject( modMeta ) JsonConvert.SerializeObject( modMeta )
); );
ExtractSimpleModList( newModFolder, modList, modData ); ExtractSimpleModList( ExtractedDirectory, modList, modData );
} }
private void ImportV2ModPack( FileInfo modPackFile, ZipFile extractedModPack, string modRaw ) private void ImportV2ModPack( FileInfo modPackFile, ZipFile extractedModPack, string modRaw )
@ -228,12 +230,12 @@ namespace Penumbra.Importer
// Open the mod data file from the modpack as a SqPackStream // Open the mod data file from the modpack as a SqPackStream
using var modData = GetMagicSqPackDeleterStream( extractedModPack, "TTMPD.mpd" ); using var modData = GetMagicSqPackDeleterStream( extractedModPack, "TTMPD.mpd" );
var newModFolder = CreateModFolder( _outDirectory, modList.Name ?? "New Mod" ); ExtractedDirectory = CreateModFolder( _outDirectory, modList.Name ?? "New Mod" );
File.WriteAllText( Path.Combine( newModFolder.FullName, "meta.json" ), File.WriteAllText( Path.Combine( ExtractedDirectory.FullName, "meta.json" ),
JsonConvert.SerializeObject( modMeta ) ); JsonConvert.SerializeObject( modMeta ) );
ExtractSimpleModList( newModFolder, modList.SimpleModsList ?? Enumerable.Empty< SimpleMod >(), modData ); ExtractSimpleModList( ExtractedDirectory, modList.SimpleModsList ?? Enumerable.Empty< SimpleMod >(), modData );
} }
private void ImportExtendedV2ModPack( ZipFile extractedModPack, string modRaw ) private void ImportExtendedV2ModPack( ZipFile extractedModPack, string modRaw )
@ -256,11 +258,11 @@ namespace Penumbra.Importer
// Open the mod data file from the modpack as a SqPackStream // Open the mod data file from the modpack as a SqPackStream
using var modData = GetMagicSqPackDeleterStream( extractedModPack, "TTMPD.mpd" ); using var modData = GetMagicSqPackDeleterStream( extractedModPack, "TTMPD.mpd" );
var newModFolder = CreateModFolder( _outDirectory, modList.Name ?? "New Mod" ); ExtractedDirectory = CreateModFolder( _outDirectory, modList.Name ?? "New Mod" );
if( modList.SimpleModsList != null ) if( modList.SimpleModsList != null )
{ {
ExtractSimpleModList( newModFolder, modList.SimpleModsList, modData ); ExtractSimpleModList( ExtractedDirectory, modList.SimpleModsList, modData );
} }
if( modList.ModPackPages == null ) if( modList.ModPackPages == null )
@ -278,7 +280,7 @@ namespace Penumbra.Importer
foreach( var group in page.ModGroups.Where( group => group.GroupName != null && group.OptionList != null ) ) foreach( var group in page.ModGroups.Where( group => group.GroupName != null && group.OptionList != null ) )
{ {
var groupFolder = NewOptionDirectory( newModFolder, group.GroupName! ); var groupFolder = NewOptionDirectory( ExtractedDirectory, group.GroupName! );
if( groupFolder.Exists ) if( groupFolder.Exists )
{ {
groupFolder = new DirectoryInfo( groupFolder.FullName + $" ({page.PageIndex})" ); groupFolder = new DirectoryInfo( groupFolder.FullName + $" ({page.PageIndex})" );
@ -291,12 +293,12 @@ namespace Penumbra.Importer
ExtractSimpleModList( optionFolder, option.ModsJsons!, modData ); ExtractSimpleModList( optionFolder, option.ModsJsons!, modData );
} }
AddMeta( newModFolder, groupFolder, group, modMeta ); AddMeta( ExtractedDirectory, groupFolder, group, modMeta );
} }
} }
File.WriteAllText( File.WriteAllText(
Path.Combine( newModFolder.FullName, "meta.json" ), Path.Combine( ExtractedDirectory.FullName, "meta.json" ),
JsonConvert.SerializeObject( modMeta, Formatting.Indented ) JsonConvert.SerializeObject( modMeta, Formatting.Indented )
); );
} }

View file

@ -13,8 +13,18 @@ using GameData = Penumbra.Game.Enums.GameData;
namespace Penumbra.Importer namespace Penumbra.Importer
{ {
// TexTools provices custom generated *.meta files for its modpacks, that contain changes to
// - imc files
// - eqp files
// - gmp files
// - est files
// - eqdp files
// made by the mod. The filename determines to what the changes are applied, and the binary file itself contains changes.
// We parse every *.meta file in a mod and combine all actual changes that do not keep data on default values and that can be applied to the game in a .json.
// TexTools may also generate files that contain non-existing changes, e.g. *.imc files for weapon offhands, which will be ignored.
public class TexToolsMeta public class TexToolsMeta
{ {
// The info class determines the files or table locations the changes need to apply to from the filename.
public class Info public class Info
{ {
private const string Pt = @"(?'PrimaryType'[a-z]*)"; // language=regex private const string Pt = @"(?'PrimaryType'[a-z]*)"; // language=regex
@ -28,6 +38,7 @@ namespace Penumbra.Importer
private const string Slot = @"(_(?'Slot'[a-z]{3}))?"; // language=regex private const string Slot = @"(_(?'Slot'[a-z]{3}))?"; // language=regex
private const string Ext = @"\.meta"; private const string Ext = @"\.meta";
// These are the valid regexes for .meta files that we are able to support at the moment.
private static readonly Regex HousingMeta = new( $"bgcommon/hou/{Pt}/general/{Pi}/{Pir}{Ext}" ); private static readonly Regex HousingMeta = new( $"bgcommon/hou/{Pt}/general/{Pi}/{Pir}{Ext}" );
private static readonly Regex CharaMeta = new( $"chara/{Pt}/{Pp}{Pi}(/obj/{St}/{Sp}{Si})?/{File}{Slot}{Ext}" ); private static readonly Regex CharaMeta = new( $"chara/{Pt}/{Pp}{Pi}(/obj/{St}/{Sp}{Si})?/{File}{Slot}{Ext}" );

View file

@ -14,6 +14,8 @@ namespace Penumbra.Meta.Files
{ } { }
} }
// Imc files are already supported in Lumina, but changing the provided data is not supported.
// We use reflection and extension methods to support changing the data of a given Imc file.
public static class ImcExtensions public static class ImcExtensions
{ {
public static ulong ToInteger( this ImcFile.ImageChangeData imc ) public static ulong ToInteger( this ImcFile.ImageChangeData imc )

View file

@ -9,6 +9,8 @@ using Penumbra.Util;
namespace Penumbra.Meta.Files namespace Penumbra.Meta.Files
{ {
// This class manages the default meta files obtained via lumina from the game files themselves.
// On first call, the default version of any supported file will be cached and can be returned without reparsing.
public class MetaDefaults public class MetaDefaults
{ {
private readonly DalamudPluginInterface _pi; private readonly DalamudPluginInterface _pi;
@ -107,6 +109,7 @@ namespace Penumbra.Meta.Files
private FileResource FetchFile( string name ) private FileResource FetchFile( string name )
=> _pi.Data.GetFile( name ); => _pi.Data.GetFile( name );
// Check that a given meta manipulation is an actual change to the default value. We don't need to keep changes to default.
public bool CheckAgainstDefault( MetaManipulation m ) public bool CheckAgainstDefault( MetaManipulation m )
{ {
return m.Type switch return m.Type switch
@ -129,6 +132,7 @@ namespace Penumbra.Meta.Files
}; };
} }
// Create a deep copy of a default file as a new file.
public object? CreateNewFile( MetaManipulation m ) public object? CreateNewFile( MetaManipulation m )
{ {
return m.Type switch return m.Type switch

View file

@ -4,6 +4,7 @@ using Penumbra.Util;
namespace Penumbra.Meta.Files namespace Penumbra.Meta.Files
{ {
// Contains all filenames for meta changes depending on their parameters.
public static class MetaFileNames public static class MetaFileNames
{ {
public static GamePath Eqp() public static GamePath Eqp()

View file

@ -1,6 +1,10 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Penumbra.Game.Enums; using Penumbra.Game.Enums;
// A struct for each type of meta change that contains all relevant information,
// to uniquely identify the corresponding file and location for the change.
// The first byte is guaranteed to be the MetaType enum for each case.
namespace Penumbra.Meta namespace Penumbra.Meta
{ {
public enum MetaType : byte public enum MetaType : byte

View file

@ -12,6 +12,7 @@ using ImcFile = Lumina.Data.Files.ImcFile;
namespace Penumbra.Meta namespace Penumbra.Meta
{ {
// Write a single meta manipulation as a Base64string of the 16 bytes defining it.
public class MetaManipulationConverter : JsonConverter< MetaManipulation > public class MetaManipulationConverter : JsonConverter< MetaManipulation >
{ {
public override void WriteJson( JsonWriter writer, MetaManipulation manip, JsonSerializer serializer ) public override void WriteJson( JsonWriter writer, MetaManipulation manip, JsonSerializer serializer )
@ -38,6 +39,10 @@ namespace Penumbra.Meta
} }
} }
// A MetaManipulation is a union of a type of Identifier (first 8 bytes, cf. Identifier.cs)
// and the appropriate Value to change the meta entry to (the other 8 bytes).
// Its comparison for sorting and hashes depends only on the identifier.
// The first byte is guaranteed to be a MetaType enum value in any case, so Type can always be read.
[StructLayout( LayoutKind.Explicit )] [StructLayout( LayoutKind.Explicit )]
[JsonConverter( typeof( MetaManipulationConverter ) )] [JsonConverter( typeof( MetaManipulationConverter ) )]
public struct MetaManipulation : IComparable public struct MetaManipulation : IComparable
@ -219,11 +224,11 @@ namespace Penumbra.Meta
{ {
return Type switch return Type switch
{ {
MetaType.Eqp => $"EQP - {EqpIdentifier}", MetaType.Eqp => EqpIdentifier.ToString(),
MetaType.Eqdp => $"EQDP - {EqdpIdentifier}", MetaType.Eqdp => EqdpIdentifier.ToString(),
MetaType.Est => $"EST - {EstIdentifier}", MetaType.Est => EstIdentifier.ToString(),
MetaType.Gmp => $"GMP - {GmpIdentifier}", MetaType.Gmp => GmpIdentifier.ToString(),
MetaType.Imc => $"IMC - {ImcIdentifier}", MetaType.Imc => ImcIdentifier.ToString(),
_ => throw new InvalidEnumArgumentException(), _ => throw new InvalidEnumArgumentException(),
}; };
} }

View file

@ -4,6 +4,8 @@ using Penumbra.Util;
namespace Penumbra.Mod namespace Penumbra.Mod
{ {
// A complete Mod containing settings (i.e. dependent on a collection)
// and the resulting cache.
public class Mod public class Mod
{ {
public ModSettings Settings { get; } public ModSettings Settings { get; }

View file

@ -5,6 +5,7 @@ using Penumbra.Util;
namespace Penumbra.Mod namespace Penumbra.Mod
{ {
// The ModCache contains volatile information dependent on all current settings in a collection.
public class ModCache public class ModCache
{ {
public Dictionary< Mod, (List< GamePath > Files, List< MetaManipulation > Manipulations) > Conflicts { get; private set; } = new(); public Dictionary< Mod, (List< GamePath > Files, List< MetaManipulation > Manipulations) > Conflicts { get; private set; } = new();

View file

@ -3,6 +3,9 @@ using Dalamud.Plugin;
namespace Penumbra.Mod namespace Penumbra.Mod
{ {
// ModData contains all permanent information about a mod,
// and is independent of collections or settings.
// It only changes when the user actively changes the mod or their filesystem.
public class ModData public class ModData
{ {
public DirectoryInfo BasePath; public DirectoryInfo BasePath;

View file

@ -6,6 +6,7 @@ using Penumbra.Util;
namespace Penumbra.Mod namespace Penumbra.Mod
{ {
// Functions that do not really depend on only one component of a mod.
public static class ModFunctions public static class ModFunctions
{ {
public static bool CleanUpCollection( Dictionary< string, ModSettings > settings, IEnumerable< DirectoryInfo > modPaths ) public static bool CleanUpCollection( Dictionary< string, ModSettings > settings, IEnumerable< DirectoryInfo > modPaths )

View file

@ -58,7 +58,6 @@ namespace Penumbra.Mod
return true; return true;
} }
public static ModMeta? LoadFromFile( FileInfo filePath ) public static ModMeta? LoadFromFile( FileInfo filePath )
{ {
try try

View file

@ -5,6 +5,7 @@ using Penumbra.Structs;
namespace Penumbra.Mod namespace Penumbra.Mod
{ {
// Contains the settings for a given mod.
public class ModSettings public class ModSettings
{ {
public bool Enabled { get; set; } public bool Enabled { get; set; }

View file

@ -4,6 +4,9 @@ using Penumbra.Structs;
namespace Penumbra.Mod namespace Penumbra.Mod
{ {
// Contains settings with the option selections stored by names instead of index.
// This is meant to make them possibly more portable when we support importing collections from other users.
// Enabled does not exist, because disabled mods would not be exported in this way.
public class NamedModSettings public class NamedModSettings
{ {
public int Priority { get; set; } public int Priority { get; set; }

View file

@ -9,6 +9,10 @@ using Penumbra.Util;
namespace Penumbra.Mods namespace Penumbra.Mods
{ {
// A ModCollection is a named set of ModSettings to all of the users' installed mods.
// It is meant to be local only, and thus should always contain settings for every mod, not just the enabled ones.
// Settings to mods that are not installed anymore are kept as long as no call to CleanUnavailableSettings is made.
// Active ModCollections build a cache of currently relevant data.
public class ModCollection public class ModCollection
{ {
public const string DefaultCollection = "Default"; public const string DefaultCollection = "Default";

View file

@ -8,6 +8,8 @@ using Penumbra.Util;
namespace Penumbra.Mods namespace Penumbra.Mods
{ {
// The ModCollectionCache contains all required temporary data to use a collection.
// It will only be setup if a collection gets activated in any way.
public class ModCollectionCache public class ModCollectionCache
{ {
public readonly List< Mod.Mod > AvailableMods = new(); public readonly List< Mod.Mod > AvailableMods = new();

View file

@ -9,6 +9,11 @@ using Penumbra.Util;
namespace Penumbra.Mods namespace Penumbra.Mods
{ {
// The ModManager handles the basic mods installed to the mod directory,
// as well as all saved collections.
// It also handles manual changes to mods that require changes in all collections,
// updating the state of a mod from the filesystem,
// and collection swapping.
public class ModManager public class ModManager
{ {
private readonly Plugin _plugin; private readonly Plugin _plugin;
@ -216,7 +221,6 @@ namespace Penumbra.Mods
return true; return true;
} }
public bool UpdateMod( ModData mod, bool recomputeMeta = false ) public bool UpdateMod( ModData mod, bool recomputeMeta = false )
{ {
var oldName = mod.Meta.Name; var oldName = mod.Meta.Name;

View file

@ -9,6 +9,8 @@ using Penumbra.Structs;
namespace Penumbra.Mods namespace Penumbra.Mods
{ {
// Extracted to keep the main file a bit more clean.
// Contains all change functions on a specific mod that also require corresponding changes to collections.
public static class ModManagerEditExtensions public static class ModManagerEditExtensions
{ {
public static bool RenameMod( this ModManager manager, string newName, ModData mod ) public static bool RenameMod( this ModManager manager, string newName, ModData mod )

View file

@ -76,7 +76,7 @@ namespace Penumbra.UI.Custom
var itemWidth = ImGui.CalcItemWidth(); var itemWidth = ImGui.CalcItemWidth();
ImGui.PushItemWidth( Math.Max( 0f, itemWidth - frameHeight ) ); ImGui.PushItemWidth( Math.Max( 0f, itemWidth - frameHeight ) );
labelStack.Add( ( labelMin, labelMax ) ); LabelStack.Add( ( labelMin, labelMax ) );
return ret; return ret;
} }
@ -111,8 +111,8 @@ namespace Penumbra.UI.Custom
var itemMin = ImGui.GetItemRectMin(); var itemMin = ImGui.GetItemRectMin();
var itemMax = ImGui.GetItemRectMax(); var itemMax = ImGui.GetItemRectMax();
var (currentLabelMin, currentLabelMax) = labelStack[ labelStack.Count - 1 ]; var (currentLabelMin, currentLabelMax) = LabelStack[ LabelStack.Count - 1 ];
labelStack.RemoveAt( labelStack.Count - 1 ); LabelStack.RemoveAt( LabelStack.Count - 1 );
var halfFrame = new Vector2( frameHeight / 8, frameHeight / 2 ); var halfFrame = new Vector2( frameHeight / 8, frameHeight / 2 );
currentLabelMin.X -= itemSpacing.X; currentLabelMin.X -= itemSpacing.X;
@ -143,6 +143,6 @@ namespace Penumbra.UI.Custom
private static readonly Vector2 ZeroVector = new( 0, 0 ); private static readonly Vector2 ZeroVector = new( 0, 0 );
private static readonly List< (Vector2, Vector2) > labelStack = new(); private static readonly List< (Vector2, Vector2) > LabelStack = new();
} }
} }

View file

@ -76,8 +76,13 @@ namespace Penumbra.UI
} }
} }
var directory = _texToolsImport?.ExtractedDirectory;
_texToolsImport = null; _texToolsImport = null;
_base.ReloadMods(); _base.ReloadMods();
if( directory != null )
{
_base._menu.InstalledTab.Selector.SelectModByDir( directory.Name );
}
} }
} }
catch( Exception e ) catch( Exception e )

View file

@ -88,7 +88,7 @@ namespace Penumbra.UI
{ {
_base = ui; _base = ui;
_modNamesLower = Array.Empty< string >(); _modNamesLower = Array.Empty< string >();
_modManager = Service<ModManager>.Get(); _modManager = Service< ModManager >.Get();
ResetModNamesLower(); ResetModNamesLower();
} }