Test Penumbra loading CharacterUtility.

This commit is contained in:
Ottermandias 2022-06-27 15:19:58 +02:00
parent 32cf729aa8
commit 5aeff6d40f
5 changed files with 92 additions and 84 deletions

View file

@ -62,13 +62,12 @@ public unsafe class CharacterUtility : IDisposable
public CharacterUtility() public CharacterUtility()
{ {
SignatureHelper.Initialise( this ); SignatureHelper.Initialise( this );
LoadingFinished += () => PluginLog.Debug( "Loading of CharacterUtility finished." );
Dalamud.Framework.Update += LoadDefaultResources; LoadDefaultResources( true );
LoadingFinished += () => PluginLog.Debug( "Loading of CharacterUtility finished." );
} }
// We store the default data of the resources so we can always restore them. // 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; var missingCount = 0;
if( Address == null ) if( Address == null )
@ -95,10 +94,15 @@ public unsafe class CharacterUtility : IDisposable
if( missingCount == 0 ) if( missingCount == 0 )
{ {
Dalamud.Framework.Update -= LoadDefaultResources; Ready = true;
Ready = true;
LoadingFinished.Invoke(); 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. // Set the data of one of the stored resources to a given pointer and length.

View file

@ -29,7 +29,8 @@ public unsafe partial class ResourceLoader
[FieldOffset( 20 )] [FieldOffset( 20 )]
public uint SegmentLength; public uint SegmentLength;
public bool IsPartialRead => SegmentLength != 0; public bool IsPartialRead
=> SegmentLength != 0;
} }
public delegate ResourceHandle* GetResourceSyncPrototype( ResourceManager* resourceManager, ResourceCategory* pCategoryId, public delegate ResourceHandle* GetResourceSyncPrototype( ResourceManager* resourceManager, ResourceCategory* pCategoryId,
@ -83,12 +84,25 @@ public unsafe partial class ResourceLoader
ResourceRequested?.Invoke( gamePath, isSync ); 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. // If no replacements are being made, we still want to be able to trigger the event.
var (resolvedPath, data) = ResolvePath( gamePath, *categoryId, *resourceType, *resourceHash ); var (resolvedPath, data) = ResolvePath( gamePath, *categoryId, *resourceType, *resourceHash );
PathResolved?.Invoke( gamePath, *resourceType, resolvedPath, data ); PathResolved?.Invoke( gamePath, *resourceType, resolvedPath, data );
if( resolvedPath == null ) 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 ); ResourceLoaded?.Invoke( ( Structs.ResourceHandle* )retUnmodified, gamePath, null, data );
return retUnmodified; return retUnmodified;
} }
@ -247,13 +261,15 @@ public unsafe partial class ResourceLoader
private int ComputeHash( Utf8String path, GetResourceParameters* pGetResParams ) private int ComputeHash( Utf8String path, GetResourceParameters* pGetResParams )
{ {
if( pGetResParams == null || !pGetResParams->IsPartialRead ) if( pGetResParams == null || !pGetResParams->IsPartialRead )
{
return path.Crc32; return path.Crc32;
}
// When the game requests file only partially, crc32 includes that information, in format of: // When the game requests file only partially, crc32 includes that information, in format of:
// path/to/file.ext.hex_offset.hex_size // path/to/file.ext.hex_offset.hex_size
// ex) music/ex4/BGM_EX4_System_Title.scd.381adc.30000 // ex) music/ex4/BGM_EX4_System_Title.scd.381adc.30000
return Utf8String.Join( return Utf8String.Join(
(byte)'.', ( byte )'.',
path, path,
Utf8String.FromStringUnsafe( pGetResParams->SegmentOffset.ToString( "x" ), true ), Utf8String.FromStringUnsafe( pGetResParams->SegmentOffset.ToString( "x" ), true ),
Utf8String.FromStringUnsafe( pGetResParams->SegmentLength.ToString( "x" ), true ) Utf8String.FromStringUnsafe( pGetResParams->SegmentLength.ToString( "x" ), true )

View file

@ -22,75 +22,16 @@ using Penumbra.Collections;
using Penumbra.Interop.Loader; using Penumbra.Interop.Loader;
using Penumbra.Interop.Resolver; using Penumbra.Interop.Resolver;
using Penumbra.Mods; using Penumbra.Mods;
using CharacterUtility = Penumbra.Interop.CharacterUtility;
using ResidentResourceManager = Penumbra.Interop.ResidentResourceManager;
namespace Penumbra; 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 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"; private const string CommandName = "/penumbra";
public static readonly string Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? string.Empty; 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 = public static readonly string CommitHash =
Assembly.GetExecutingAssembly().GetCustomAttribute< AssemblyInformationalVersionAttribute >()?.InformationalVersion ?? "Unknown"; Assembly.GetExecutingAssembly().GetCustomAttribute< AssemblyInformationalVersionAttribute >()?.InformationalVersion ?? "Unknown";
public static bool DevPenumbraExists;
public static bool IsNotInstalledPenumbra;
public static Configuration Config { get; private set; } = null!; public static Configuration Config { get; private set; } = null!;
public static ResidentResourceManager ResidentResources { get; private set; } = null!; public static ResidentResourceManager ResidentResources { get; private set; } = null!;
@ -122,9 +66,12 @@ public class Penumbra : IDisposable
internal WebServer? WebServer; 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(); Framework = new FrameworkManager();
Backup.CreateBackup( PenumbraBackupFiles() ); Backup.CreateBackup( PenumbraBackupFiles() );
@ -134,8 +81,10 @@ public class Penumbra : IDisposable
TempMods = new TempModManager(); TempMods = new TempModManager();
MetaFileManager = new MetaFileManager(); MetaFileManager = new MetaFileManager();
ResourceLoader = new ResourceLoader( this ); ResourceLoader = new ResourceLoader( this );
ResourceLogger = new ResourceLogger( ResourceLoader ); ResourceLoader.EnableHooks();
ModManager = new Mod.Manager( Config.ModDirectory ); ResourceLogger = new ResourceLogger( ResourceLoader );
CharacterUtility = new CharacterUtility();
ModManager = new Mod.Manager( Config.ModDirectory );
ModManager.DiscoverMods(); ModManager.DiscoverMods();
CollectionManager = new ModCollection.Manager( ModManager ); CollectionManager = new ModCollection.Manager( ModManager );
ModFileSystem = ModFileSystem.Load(); ModFileSystem = ModFileSystem.Load();
@ -151,18 +100,17 @@ public class Penumbra : IDisposable
SetupInterface( out _configWindow, out _launchButton, out _windowSystem ); SetupInterface( out _configWindow, out _launchButton, out _windowSystem );
if( Config.EnableHttpApi )
{
CreateWebServer();
}
ResourceLoader.EnableHooks();
if( Config.EnableMods ) if( Config.EnableMods )
{ {
ResourceLoader.EnableReplacements(); ResourceLoader.EnableReplacements();
PathResolver.Enable(); PathResolver.Enable();
} }
if( Config.EnableHttpApi )
{
CreateWebServer();
}
if( Config.DebugMode ) if( Config.DebugMode )
{ {
ResourceLoader.EnableDebug(); ResourceLoader.EnableDebug();
@ -508,8 +456,11 @@ public class Penumbra : IDisposable
{ {
var collection = CollectionManager.ByType( type ); var collection = CollectionManager.ByType( type );
if( collection != null ) if( collection != null )
{
sb.AppendFormat( "> **`{0,-29}`** {1}... ({2})\n", type.ToName(), CollectionName( collection ), collection.Index ); sb.AppendFormat( "> **`{0,-29}`** {1}... ({2})\n", type.ToName(), CollectionName( collection ), collection.Index );
}
} }
foreach( var (name, collection) in CollectionManager.Characters ) foreach( var (name, collection) in CollectionManager.Characters )
{ {
sb.AppendFormat( "> **`{2,-29}`** {0}... ({1})\n", CollectionName( collection ), sb.AppendFormat( "> **`{2,-29}`** {0}... ({1})\n", CollectionName( collection ),
@ -523,4 +474,39 @@ public class Penumbra : IDisposable
return sb.ToString(); 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
}
} }

View file

@ -9,5 +9,7 @@
"Tags": [ "modding" ], "Tags": [ "modding" ],
"DalamudApiLevel": 6, "DalamudApiLevel": 6,
"LoadPriority": 69420, "LoadPriority": 69420,
"LoadRequiredState": 2,
"LoadSync": true,
"IconUrl": "https://raw.githubusercontent.com/xivdev/Penumbra/master/images/icon.png" "IconUrl": "https://raw.githubusercontent.com/xivdev/Penumbra/master/images/icon.png"
} }

View file

@ -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" + "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." ); + "Please use the Launcher's Repair Game Files function to repair your client installation." );
} }
else if( MainClass.IsNotInstalledPenumbra ) else if( Penumbra.IsNotInstalledPenumbra )
{ {
DrawProblemWindow( DrawProblemWindow(
$"You are loading a release version of Penumbra from \"{Dalamud.PluginInterface.AssemblyLocation.Directory?.FullName ?? "Unknown"}\" instead of the installedPlugins directory.\n\n" $"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 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." ); + "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( DrawProblemWindow(
$"You are loading a installed version of Penumbra from \"{Dalamud.PluginInterface.AssemblyLocation.Directory?.FullName ?? "Unknown"}\", " $"You are loading a installed version of Penumbra from \"{Dalamud.PluginInterface.AssemblyLocation.Directory?.FullName ?? "Unknown"}\", "