From a1504046c214a30dc7f18b5a987a85ebcee6fcfc Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Thu, 12 Aug 2021 14:47:07 +0200 Subject: [PATCH] Add resolving file paths and obtaining files through Penumbra to the API. --- Penumbra.Api/IPenumbraApi.cs | 23 +++++++++++ Penumbra.Api/Penumbra.Api.csproj | 4 ++ Penumbra/Api/PenumbraApi.cs | 71 ++++++++++++++++++++++++++++++-- 3 files changed, 94 insertions(+), 4 deletions(-) diff --git a/Penumbra.Api/IPenumbraApi.cs b/Penumbra.Api/IPenumbraApi.cs index a77b4347..4eb8867e 100644 --- a/Penumbra.Api/IPenumbraApi.cs +++ b/Penumbra.Api/IPenumbraApi.cs @@ -1,4 +1,5 @@ using Dalamud.Game.ClientState.Actors.Types; +using Lumina.Data; namespace Penumbra.Api { @@ -21,11 +22,33 @@ namespace Penumbra.Api public interface IPenumbraApi : IPenumbraApiBase { + // Triggered when the user hovers over a listed changed object in a mod tab. + // Can be used to append tooltips. public event ChangedItemHover? ChangedItemTooltip; + // Triggered when the user clicks a listed changed object in a mod tab. public event ChangedItemClick? ChangedItemClicked; + // Queue redrawing of all actors of the given name with the given RedrawType. public void RedrawActor( string name, RedrawType setting ); + + // Queue redrawing of the specific actor with the given RedrawType. Should only be used when the actor is sure to be valid. public void RedrawActor( Actor actor, RedrawType setting ); + + // Queue redrawing of all currently available actors with the given RedrawType. public void RedrawAll( RedrawType setting ); + + // Resolve a given gamePath via Penumbra using the Default and Forced collections. + // Returns the given gamePath if penumbra would not manipulate it. + public string ResolvePath(string gamePath); + + // Resolve a given gamePath via Penumbra using the character collection for the given name (if it exists) and the Forced collections. + // Returns the given gamePath if penumbra would not manipulate it. + public string ResolvePath( string gamePath, string characterName ); + + // Try to load a given gamePath with the resolved path from Penumbra. + public T? GetFile< T >( string gamePath ) where T : FileResource; + + // Try to load a given gamePath with the resolved path from Penumbra. + public T? GetFile( string gamePath, string characterName ) where T : FileResource; } } \ No newline at end of file diff --git a/Penumbra.Api/Penumbra.Api.csproj b/Penumbra.Api/Penumbra.Api.csproj index 08b23127..8a4f0611 100644 --- a/Penumbra.Api/Penumbra.Api.csproj +++ b/Penumbra.Api/Penumbra.Api.csproj @@ -33,5 +33,9 @@ $(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll False + + $(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.dll + False + \ No newline at end of file diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index 3affe8a8..97e2ed4b 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -1,18 +1,29 @@ using System; +using System.IO; +using System.Reflection; using Dalamud.Game.ClientState.Actors.Types; +using Dalamud.Plugin; +using Lumina.Data; +using Penumbra.GameData.Util; +using Penumbra.Mods; +using Penumbra.Util; namespace Penumbra.Api { public class PenumbraApi : IDisposable, IPenumbraApi { - public int ApiVersion { get; } = 1; - private readonly Plugin _plugin; + public int ApiVersion { get; } = 2; + private readonly Plugin _plugin; + private readonly Lumina.GameData? _lumina; public bool Valid { get; private set; } = false; public PenumbraApi( Plugin penumbra ) { - _plugin = penumbra; - Valid = true; + _plugin = penumbra; + Valid = true; + _lumina = ( Lumina.GameData? )_plugin.PluginInterface.Data.GetType() + .GetField( "gameData", BindingFlags.Instance | BindingFlags.NonPublic ) + ?.GetValue( _plugin.PluginInterface.Data ); } public void Dispose() @@ -61,5 +72,57 @@ namespace Penumbra.Api _plugin.ActorRefresher.RedrawAll( setting ); } + + private string ResolvePath( string path, ModManager manager, ModCollection collection ) + { + if( !_plugin.Configuration.IsEnabled ) + { + return path; + } + var gamePath = new GamePath( path ); + var ret = collection.Cache?.ResolveSwappedOrReplacementPath( gamePath ); + ret ??= manager.Collections.ForcedCollection.Cache?.ResolveSwappedOrReplacementPath( gamePath ); + ret ??= path; + return ret; + } + + public string ResolvePath( string path ) + { + var modManager = Service< ModManager >.Get(); + return ResolvePath( path, modManager, modManager.Collections.DefaultCollection ); + } + + public string ResolvePath( string path, string characterName ) + { + var modManager = Service< ModManager >.Get(); + return ResolvePath( path, modManager, + modManager.Collections.CharacterCollection.TryGetValue( characterName, out var collection ) + ? collection + : ModCollection.Empty ); + } + + private T? GetFileIntern< T >( string resolvedPath ) where T : FileResource + { + try + { + if( Path.IsPathRooted( resolvedPath ) ) + { + return _lumina?.GetFileFromDisk< T >( resolvedPath ); + } + + return _plugin.PluginInterface.Data.GetFile< T >( resolvedPath ); + } + catch( Exception e) + { + PluginLog.Warning( $"Could not load file {resolvedPath}:\n{e}" ); + return null; + } + } + + public T? GetFile< T >( string gamePath ) where T : FileResource + => GetFileIntern< T >( ResolvePath( gamePath ) ); + + public T? GetFile< T >( string gamePath, string characterName ) where T : FileResource + => GetFileIntern< T >( ResolvePath( gamePath, characterName ) ); } } \ No newline at end of file