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,
}
// The combined gender-race-npc numerical code as used by the game.
public enum GenderRace : ushort
{
Unknown = 0,

View file

@ -62,6 +62,7 @@ namespace Penumbra.Hooks
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()
{
ReloadCharacterResources();

View file

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

View file

@ -21,6 +21,8 @@ namespace Penumbra.Importer
private const string TempFileName = "textools-import";
private readonly string _resolvedTempFilePath;
public DirectoryInfo? ExtractedDirectory { get; private set; }
public ImporterState State { 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
using var modData = GetMagicSqPackDeleterStream( extractedModPack, "TTMPD.mpd" );
var newModFolder = CreateModFolder( _outDirectory, Path.GetFileNameWithoutExtension( modPackFile.Name ) );
ExtractedDirectory = CreateModFolder( _outDirectory, Path.GetFileNameWithoutExtension( modPackFile.Name ) );
File.WriteAllText(
Path.Combine( newModFolder.FullName, "meta.json" ),
Path.Combine( ExtractedDirectory.FullName, "meta.json" ),
JsonConvert.SerializeObject( modMeta )
);
ExtractSimpleModList( newModFolder, modList, modData );
ExtractSimpleModList( ExtractedDirectory, modList, modData );
}
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
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 ) );
ExtractSimpleModList( newModFolder, modList.SimpleModsList ?? Enumerable.Empty< SimpleMod >(), modData );
ExtractSimpleModList( ExtractedDirectory, modList.SimpleModsList ?? Enumerable.Empty< SimpleMod >(), modData );
}
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
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 )
{
ExtractSimpleModList( newModFolder, modList.SimpleModsList, modData );
ExtractSimpleModList( ExtractedDirectory, modList.SimpleModsList, modData );
}
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 ) )
{
var groupFolder = NewOptionDirectory( newModFolder, group.GroupName! );
var groupFolder = NewOptionDirectory( ExtractedDirectory, group.GroupName! );
if( groupFolder.Exists )
{
groupFolder = new DirectoryInfo( groupFolder.FullName + $" ({page.PageIndex})" );
@ -291,12 +293,12 @@ namespace Penumbra.Importer
ExtractSimpleModList( optionFolder, option.ModsJsons!, modData );
}
AddMeta( newModFolder, groupFolder, group, modMeta );
AddMeta( ExtractedDirectory, groupFolder, group, modMeta );
}
}
File.WriteAllText(
Path.Combine( newModFolder.FullName, "meta.json" ),
Path.Combine( ExtractedDirectory.FullName, "meta.json" ),
JsonConvert.SerializeObject( modMeta, Formatting.Indented )
);
}

View file

@ -13,8 +13,18 @@ using GameData = Penumbra.Game.Enums.GameData;
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
{
// The info class determines the files or table locations the changes need to apply to from the filename.
public class Info
{
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 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 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 ulong ToInteger( this ImcFile.ImageChangeData imc )

View file

@ -9,6 +9,8 @@ using Penumbra.Util;
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
{
private readonly DalamudPluginInterface _pi;
@ -107,6 +109,7 @@ namespace Penumbra.Meta.Files
private FileResource FetchFile( string 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 )
{
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 )
{
return m.Type switch

View file

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

View file

@ -1,6 +1,10 @@
using System.Runtime.InteropServices;
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
{
public enum MetaType : byte

View file

@ -12,6 +12,7 @@ using ImcFile = Lumina.Data.Files.ImcFile;
namespace Penumbra.Meta
{
// Write a single meta manipulation as a Base64string of the 16 bytes defining it.
public class MetaManipulationConverter : JsonConverter< MetaManipulation >
{
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 )]
[JsonConverter( typeof( MetaManipulationConverter ) )]
public struct MetaManipulation : IComparable
@ -219,11 +224,11 @@ namespace Penumbra.Meta
{
return Type switch
{
MetaType.Eqp => $"EQP - {EqpIdentifier}",
MetaType.Eqdp => $"EQDP - {EqdpIdentifier}",
MetaType.Est => $"EST - {EstIdentifier}",
MetaType.Gmp => $"GMP - {GmpIdentifier}",
MetaType.Imc => $"IMC - {ImcIdentifier}",
MetaType.Eqp => EqpIdentifier.ToString(),
MetaType.Eqdp => EqdpIdentifier.ToString(),
MetaType.Est => EstIdentifier.ToString(),
MetaType.Gmp => GmpIdentifier.ToString(),
MetaType.Imc => ImcIdentifier.ToString(),
_ => throw new InvalidEnumArgumentException(),
};
}

View file

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

View file

@ -5,6 +5,7 @@ using Penumbra.Util;
namespace Penumbra.Mod
{
// The ModCache contains volatile information dependent on all current settings in a collection.
public class ModCache
{
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
{
// 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 DirectoryInfo BasePath;

View file

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

View file

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

View file

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

View file

@ -4,6 +4,9 @@ using Penumbra.Structs;
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 int Priority { get; set; }

View file

@ -9,6 +9,10 @@ using Penumbra.Util;
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 const string DefaultCollection = "Default";

View file

@ -8,6 +8,8 @@ using Penumbra.Util;
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 readonly List< Mod.Mod > AvailableMods = new();

View file

@ -9,6 +9,11 @@ using Penumbra.Util;
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
{
private readonly Plugin _plugin;
@ -216,7 +221,6 @@ namespace Penumbra.Mods
return true;
}
public bool UpdateMod( ModData mod, bool recomputeMeta = false )
{
var oldName = mod.Meta.Name;

View file

@ -9,6 +9,8 @@ using Penumbra.Structs;
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 bool RenameMod( this ModManager manager, string newName, ModData mod )

View file

@ -76,7 +76,7 @@ namespace Penumbra.UI.Custom
var itemWidth = ImGui.CalcItemWidth();
ImGui.PushItemWidth( Math.Max( 0f, itemWidth - frameHeight ) );
labelStack.Add( ( labelMin, labelMax ) );
LabelStack.Add( ( labelMin, labelMax ) );
return ret;
}
@ -111,8 +111,8 @@ namespace Penumbra.UI.Custom
var itemMin = ImGui.GetItemRectMin();
var itemMax = ImGui.GetItemRectMax();
var (currentLabelMin, currentLabelMax) = labelStack[ labelStack.Count - 1 ];
labelStack.RemoveAt( labelStack.Count - 1 );
var (currentLabelMin, currentLabelMax) = LabelStack[ LabelStack.Count - 1 ];
LabelStack.RemoveAt( LabelStack.Count - 1 );
var halfFrame = new Vector2( frameHeight / 8, frameHeight / 2 );
currentLabelMin.X -= itemSpacing.X;
@ -143,6 +143,6 @@ namespace Penumbra.UI.Custom
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;
_base.ReloadMods();
if( directory != null )
{
_base._menu.InstalledTab.Selector.SelectModByDir( directory.Name );
}
}
}
catch( Exception e )

View file

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