From 5aeff6d40f9caa0e23965193f454a1070542eb39 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 27 Jun 2022 15:19:58 +0200 Subject: [PATCH] Test Penumbra loading CharacterUtility. --- Penumbra/Interop/CharacterUtility.cs | 16 ++- .../Loader/ResourceLoader.Replacement.cs | 22 ++- Penumbra/Penumbra.cs | 132 ++++++++---------- Penumbra/Penumbra.json | 2 + Penumbra/UI/ConfigWindow.cs | 4 +- 5 files changed, 92 insertions(+), 84 deletions(-) diff --git a/Penumbra/Interop/CharacterUtility.cs b/Penumbra/Interop/CharacterUtility.cs index 1c26ebb8..0db41cd7 100644 --- a/Penumbra/Interop/CharacterUtility.cs +++ b/Penumbra/Interop/CharacterUtility.cs @@ -62,13 +62,12 @@ public unsafe class CharacterUtility : IDisposable public CharacterUtility() { SignatureHelper.Initialise( this ); - - Dalamud.Framework.Update += LoadDefaultResources; - LoadingFinished += () => PluginLog.Debug( "Loading of CharacterUtility finished." ); + LoadingFinished += () => PluginLog.Debug( "Loading of CharacterUtility finished." ); + LoadDefaultResources( true ); } // We store the default data of the resources so we can always restore them. - private void LoadDefaultResources( object _ ) + private void LoadDefaultResources( bool repeat ) { var missingCount = 0; if( Address == null ) @@ -95,10 +94,15 @@ public unsafe class CharacterUtility : IDisposable if( missingCount == 0 ) { - Dalamud.Framework.Update -= LoadDefaultResources; - Ready = true; + Ready = true; LoadingFinished.Invoke(); } + else if( repeat ) + { + PluginLog.Debug( "Custom load of character resources triggered." ); + LoadCharacterResources(); + LoadDefaultResources( false ); + } } // Set the data of one of the stored resources to a given pointer and length. diff --git a/Penumbra/Interop/Loader/ResourceLoader.Replacement.cs b/Penumbra/Interop/Loader/ResourceLoader.Replacement.cs index 8390d653..ee70d33c 100644 --- a/Penumbra/Interop/Loader/ResourceLoader.Replacement.cs +++ b/Penumbra/Interop/Loader/ResourceLoader.Replacement.cs @@ -29,7 +29,8 @@ public unsafe partial class ResourceLoader [FieldOffset( 20 )] public uint SegmentLength; - public bool IsPartialRead => SegmentLength != 0; + public bool IsPartialRead + => SegmentLength != 0; } public delegate ResourceHandle* GetResourceSyncPrototype( ResourceManager* resourceManager, ResourceCategory* pCategoryId, @@ -83,12 +84,25 @@ public unsafe partial class ResourceLoader ResourceRequested?.Invoke( gamePath, isSync ); + // Force metadata tables to load synchronously and not be able to be replaced. + switch( *resourceType ) + { + case ResourceType.Eqp: + case ResourceType.Gmp: + case ResourceType.Eqdp: + case ResourceType.Cmp: + case ResourceType.Est: + PluginLog.Verbose( "Forced resource {gamePath} to be loaded synchronously.", gamePath ); + return CallOriginalHandler( true, resourceManager, categoryId, resourceType, resourceHash, path, pGetResParams, isUnk ); + } + // If no replacements are being made, we still want to be able to trigger the event. var (resolvedPath, data) = ResolvePath( gamePath, *categoryId, *resourceType, *resourceHash ); PathResolved?.Invoke( gamePath, *resourceType, resolvedPath, data ); if( resolvedPath == null ) { - var retUnmodified = CallOriginalHandler( isSync, resourceManager, categoryId, resourceType, resourceHash, path, pGetResParams, isUnk ); + var retUnmodified = + CallOriginalHandler( isSync, resourceManager, categoryId, resourceType, resourceHash, path, pGetResParams, isUnk ); ResourceLoaded?.Invoke( ( Structs.ResourceHandle* )retUnmodified, gamePath, null, data ); return retUnmodified; } @@ -247,13 +261,15 @@ public unsafe partial class ResourceLoader private int ComputeHash( Utf8String path, GetResourceParameters* pGetResParams ) { if( pGetResParams == null || !pGetResParams->IsPartialRead ) + { return path.Crc32; + } // When the game requests file only partially, crc32 includes that information, in format of: // path/to/file.ext.hex_offset.hex_size // ex) music/ex4/BGM_EX4_System_Title.scd.381adc.30000 return Utf8String.Join( - (byte)'.', + ( byte )'.', path, Utf8String.FromStringUnsafe( pGetResParams->SegmentOffset.ToString( "x" ), true ), Utf8String.FromStringUnsafe( pGetResParams->SegmentLength.ToString( "x" ), true ) diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index 993b5edc..696ab08b 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -22,75 +22,16 @@ using Penumbra.Collections; using Penumbra.Interop.Loader; using Penumbra.Interop.Resolver; using Penumbra.Mods; +using CharacterUtility = Penumbra.Interop.CharacterUtility; +using ResidentResourceManager = Penumbra.Interop.ResidentResourceManager; namespace Penumbra; -public class MainClass : IDalamudPlugin +public class Penumbra : IDalamudPlugin { - private Penumbra? _penumbra; - private readonly CharacterUtility _characterUtility; - public static bool DevPenumbraExists; - public static bool IsNotInstalledPenumbra; - - public MainClass( DalamudPluginInterface pluginInterface ) - { - Dalamud.Initialize( pluginInterface ); - DevPenumbraExists = CheckDevPluginPenumbra(); - IsNotInstalledPenumbra = CheckIsNotInstalled(); - GameData.GameData.GetIdentifier( Dalamud.GameData, Dalamud.ClientState.ClientLanguage ); - _characterUtility = new CharacterUtility(); - _characterUtility.LoadingFinished += () - => _penumbra = new Penumbra( _characterUtility ); - } - - public void Dispose() - { - _penumbra?.Dispose(); - _characterUtility.Dispose(); - } - public string Name - => Penumbra.Name; + => "Penumbra"; - // Because remnants of penumbra in devPlugins cause issues, we check for them to warn users to remove them. - private static bool CheckDevPluginPenumbra() - { -#if !DEBUG - var path = Path.Combine( Dalamud.PluginInterface.DalamudAssetDirectory.Parent?.FullName ?? "INVALIDPATH", "devPlugins", "Penumbra" ); - var dir = new DirectoryInfo( path ); - - try - { - return dir.Exists && dir.EnumerateFiles( "*.dll", SearchOption.AllDirectories ).Any(); - } - catch( Exception e ) - { - PluginLog.Error( $"Could not check for dev plugin Penumbra:\n{e}" ); - return true; - } -#else - return false; -#endif - } - - // Check if the loaded version of penumbra itself is in devPlugins. - private static bool CheckIsNotInstalled() - { -#if !DEBUG - var checkedDirectory = Dalamud.PluginInterface.AssemblyLocation.Directory?.Parent?.Parent?.Name; - var ret = checkedDirectory?.Equals( "installedPlugins", StringComparison.OrdinalIgnoreCase ) ?? false; - if (!ret) - PluginLog.Error($"Penumbra is not correctly installed. Application loaded from \"{Dalamud.PluginInterface.AssemblyLocation.Directory!.FullName}\"." ); - return !ret; -#else - return false; -#endif - } -} - -public class Penumbra : IDisposable -{ - public const string Name = "Penumbra"; private const string CommandName = "/penumbra"; public static readonly string Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? string.Empty; @@ -98,6 +39,9 @@ public class Penumbra : IDisposable public static readonly string CommitHash = Assembly.GetExecutingAssembly().GetCustomAttribute< AssemblyInformationalVersionAttribute >()?.InformationalVersion ?? "Unknown"; + public static bool DevPenumbraExists; + public static bool IsNotInstalledPenumbra; + public static Configuration Config { get; private set; } = null!; public static ResidentResourceManager ResidentResources { get; private set; } = null!; @@ -122,9 +66,12 @@ public class Penumbra : IDisposable internal WebServer? WebServer; - public Penumbra( CharacterUtility characterUtility ) + public Penumbra( DalamudPluginInterface pluginInterface ) { - CharacterUtility = characterUtility; + Dalamud.Initialize( pluginInterface ); + GameData.GameData.GetIdentifier( Dalamud.GameData, Dalamud.ClientState.ClientLanguage ); + DevPenumbraExists = CheckDevPluginPenumbra(); + IsNotInstalledPenumbra = CheckIsNotInstalled(); Framework = new FrameworkManager(); Backup.CreateBackup( PenumbraBackupFiles() ); @@ -134,8 +81,10 @@ public class Penumbra : IDisposable TempMods = new TempModManager(); MetaFileManager = new MetaFileManager(); ResourceLoader = new ResourceLoader( this ); - ResourceLogger = new ResourceLogger( ResourceLoader ); - ModManager = new Mod.Manager( Config.ModDirectory ); + ResourceLoader.EnableHooks(); + ResourceLogger = new ResourceLogger( ResourceLoader ); + CharacterUtility = new CharacterUtility(); + ModManager = new Mod.Manager( Config.ModDirectory ); ModManager.DiscoverMods(); CollectionManager = new ModCollection.Manager( ModManager ); ModFileSystem = ModFileSystem.Load(); @@ -151,18 +100,17 @@ public class Penumbra : IDisposable SetupInterface( out _configWindow, out _launchButton, out _windowSystem ); - if( Config.EnableHttpApi ) - { - CreateWebServer(); - } - - ResourceLoader.EnableHooks(); if( Config.EnableMods ) { ResourceLoader.EnableReplacements(); PathResolver.Enable(); } + if( Config.EnableHttpApi ) + { + CreateWebServer(); + } + if( Config.DebugMode ) { ResourceLoader.EnableDebug(); @@ -508,8 +456,11 @@ public class Penumbra : IDisposable { var collection = CollectionManager.ByType( type ); if( collection != null ) + { sb.AppendFormat( "> **`{0,-29}`** {1}... ({2})\n", type.ToName(), CollectionName( collection ), collection.Index ); + } } + foreach( var (name, collection) in CollectionManager.Characters ) { sb.AppendFormat( "> **`{2,-29}`** {0}... ({1})\n", CollectionName( collection ), @@ -523,4 +474,39 @@ public class Penumbra : IDisposable return sb.ToString(); } + + // Because remnants of penumbra in devPlugins cause issues, we check for them to warn users to remove them. + private static bool CheckDevPluginPenumbra() + { +#if !DEBUG + var path = Path.Combine( Dalamud.PluginInterface.DalamudAssetDirectory.Parent?.FullName ?? "INVALIDPATH", "devPlugins", "Penumbra" ); + var dir = new DirectoryInfo( path ); + + try + { + return dir.Exists && dir.EnumerateFiles( "*.dll", SearchOption.AllDirectories ).Any(); + } + catch( Exception e ) + { + PluginLog.Error( $"Could not check for dev plugin Penumbra:\n{e}" ); + return true; + } +#else + return false; +#endif + } + + // Check if the loaded version of penumbra itself is in devPlugins. + private static bool CheckIsNotInstalled() + { +#if !DEBUG + var checkedDirectory = Dalamud.PluginInterface.AssemblyLocation.Directory?.Parent?.Parent?.Name; + var ret = checkedDirectory?.Equals( "installedPlugins", StringComparison.OrdinalIgnoreCase ) ?? false; + if (!ret) + PluginLog.Error($"Penumbra is not correctly installed. Application loaded from \"{Dalamud.PluginInterface.AssemblyLocation.Directory!.FullName}\"." ); + return !ret; +#else + return false; +#endif + } } \ No newline at end of file diff --git a/Penumbra/Penumbra.json b/Penumbra/Penumbra.json index 0e05326e..d17fe004 100644 --- a/Penumbra/Penumbra.json +++ b/Penumbra/Penumbra.json @@ -9,5 +9,7 @@ "Tags": [ "modding" ], "DalamudApiLevel": 6, "LoadPriority": 69420, + "LoadRequiredState": 2, + "LoadSync": true, "IconUrl": "https://raw.githubusercontent.com/xivdev/Penumbra/master/images/icon.png" } \ No newline at end of file diff --git a/Penumbra/UI/ConfigWindow.cs b/Penumbra/UI/ConfigWindow.cs index edaaddcf..8025cf3c 100644 --- a/Penumbra/UI/ConfigWindow.cs +++ b/Penumbra/UI/ConfigWindow.cs @@ -60,7 +60,7 @@ public sealed partial class ConfigWindow : Window, IDisposable + "It is recommended to not use TexTools and Penumbra (or other Lumina-based tools) at the same time.\n\n" + "Please use the Launcher's Repair Game Files function to repair your client installation." ); } - else if( MainClass.IsNotInstalledPenumbra ) + else if( Penumbra.IsNotInstalledPenumbra ) { DrawProblemWindow( $"You are loading a release version of Penumbra from \"{Dalamud.PluginInterface.AssemblyLocation.Directory?.FullName ?? "Unknown"}\" instead of the installedPlugins directory.\n\n" @@ -68,7 +68,7 @@ public sealed partial class ConfigWindow : Window, IDisposable + "If you do not know how to do this, please take a look at the readme in Penumbras github repository or join us in discord.\n" + "If you are developing for Penumbra and see this, you should compile your version in debug mode to avoid it." ); } - else if( MainClass.DevPenumbraExists ) + else if( Penumbra.DevPenumbraExists ) { DrawProblemWindow( $"You are loading a installed version of Penumbra from \"{Dalamud.PluginInterface.AssemblyLocation.Directory?.FullName ?? "Unknown"}\", "