This commit is contained in:
2025-08-22 02:19:48 +01:00
commit a4c82452be
373 changed files with 52044 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
using Penumbra.Api.Enums;
namespace Penumbra.Api.Api;
/// <summary> API methods pertaining to collection management. </summary>
public interface IPenumbraApiCollection
{
/// <returns> A list of the GUIDs of all currently installed collections together with their display names, excluding the empty collection. </returns>
public Dictionary<Guid, string> GetCollections();
/// <summary> Returns all collections for which either
/// <list type="number">
/// <item> the name is equal to the given identifier up to case, </item>
/// <item> the identifier is parsable to a GUID and the GUID corresponds to an existing collection, </item>
/// <item> or the identifier is at least 8 characters long and the GUID as a hex-string starts with the identifier. </item>
/// </list>
/// </summary>
public List<(Guid Id, string Name)> GetCollectionsByIdentifier(string identifier);
/// <returns>A dictionary of affected items in <paramref name="collectionId"/> via GUID and known objects or null.</returns>
public Dictionary<string, object?> GetChangedItemsForCollection(Guid collectionId);
/// <returns> The GUID and name of the collection assigned to the given <paramref name="type"/>, the empty GUID for the empty collection, or null if nothing is assigned. </returns>
public (Guid Id, string Name)? GetCollection(ApiCollectionType type);
/// <returns>Return whether the object at <paramref name="gameObjectIdx" /> produces a valid identifier, if the identifier has a collection assigned, and the collection that affects the object.</returns>
public (bool ObjectValid, bool IndividualSet, (Guid Id, string Name) EffectiveCollection) GetCollectionForObject(int gameObjectIdx);
/// <summary>
/// Set a collection by GUID for a specific type.
/// </summary>
/// <param name="type">The collection type to set.</param>
/// <param name="collectionId">The GUID of the collection to set it to, null to remove the association if allowed. </param>
/// <param name="allowCreateNew">Allow only setting existing types or also creating an unset type.</param>
/// <param name="allowDelete">Allow deleting existing collections if <paramref name="collectionId"/> is empty.</param>
/// <returns>InvalidArgument if type is invalid,
/// NothingChanged if the new collection is the same as the old,<br />
/// AssignmentDeletionDisallowed if <paramref name="collectionId"/> is null and <paramref name="allowDelete"/> is false, and the assignment exists,<br />
/// or if Default, Current or Interface would be deleted.<br />
/// CollectionMissing if the new collection can not be found,<br />
/// AssignmentCreationDisallowed if <paramref name="allowCreateNew"/> is false and the assignment does not exist,<br />
/// or Success, as well as the GUID of the previous collection (empty if no assignment existed).
/// </returns>
public (PenumbraApiEc, (Guid Id, string Name)? OldCollection) SetCollection(ApiCollectionType type, Guid? collectionId, bool allowCreateNew,
bool allowDelete);
/// <summary>
/// Set a collection by GUID for a specific game object.
/// </summary>
/// <param name="gameObjectIdx">The index of the desired game object in the object table.</param>
/// <param name="collectionId">The GUID of the collection to set it to, null to remove the association if allowed. </param>
/// <param name="allowCreateNew">Allow only setting existing individuals or also creating a new individual assignment.</param>
/// <param name="allowDelete">Allow deleting existing individual assignments if <paramref name="collectionId"/> is null.</param>
/// <returns>InvalidIdentifier if <paramref name="gameObjectIdx"/> does not produce an existing game object or the object is not identifiable,
/// NothingChanged if the new collection is the same as the old,<br />
/// AssignmentDeletionDisallowed if <paramref name="collectionId"/> is null and <paramref name="allowDelete"/> is false, and the assignment exists,<br />
/// CollectionMissing if the new collection can not be found,<br />
/// AssignmentCreationDisallowed if <paramref name="allowCreateNew"/> is false and the assignment does not exist,<br />
/// or Success, as well as the name of the previous collection (empty if no assignment existed).</returns>
public (PenumbraApiEc, (Guid Id, string Name)? OldCollection) SetCollectionForObject(int gameObjectIdx, Guid? collectionId, bool allowCreateNew,
bool allowDelete);
/// <summary> Obtain a function object that can check if the current collection contains a given changed item by listing the mods changing it. </summary>
/// <remarks> Throws an <seealso cref="ObjectDisposedException"/> on invocation if the collection storage is not valid anymore, so clear this on <seealso cref="IpcSubscribers.Disposed"/>. </remarks>
public Func<string, (string ModDirectory, string ModName)[]> CheckCurrentChangedItemFunc();
}

View File

@@ -0,0 +1,28 @@
using Penumbra.Api.Enums;
namespace Penumbra.Api.Api;
/// <summary> API methods pertaining to the editing of mods or game files. </summary>
public interface IPenumbraApiEditing
{
/// <summary>
/// Convert the given texture file into a different type or format and potentially add mip maps.
/// </summary>
/// <param name="inputFile"> The path to the input file, which may be of .dds, .tex or .png format. </param>
/// <param name="outputFile"> The desired output path. Can be the same as input. </param>
/// <param name="textureType"> The file type and format to convert the data to. </param>
/// <param name="mipMaps"> Whether to add mip maps or not. Ignored for .png. </param>
/// <returns> A task for when the conversion is finished or has failed. </returns>
public Task ConvertTextureFile(string inputFile, string outputFile, TextureType textureType, bool mipMaps);
/// <summary>
/// Convert the given RGBA32 texture data into a different type or format and potentially add mip maps.
/// </summary>
/// <param name="rgbaData"> The input byte data for a picture given in RGBA32 format. </param>
/// <param name="width"> The width of the input picture. The height is computed from the size of <paramref name="rgbaData"/> and this. </param>
/// <param name="outputFile"> The desired output path. Can be the same as input. </param>
/// <param name="textureType"> The file type and format to convert the data to. </param>
/// <param name="mipMaps"> Whether to add mip maps or not. Ignored for .png. </param>
/// <returns> A task for when the conversion is finished or has failed. </returns>
public Task ConvertTextureData(byte[] rgbaData, int width, string outputFile, TextureType textureType, bool mipMaps);
}

View File

@@ -0,0 +1,52 @@
using Penumbra.Api.Enums;
namespace Penumbra.Api.Api;
/// <summary> API methods pertaining to the currently tracked game state. </summary>
public interface IPenumbraApiGameState
{
/// <param name="drawObject"></param>
/// <returns>The game object associated with the given <paramref name="drawObject">draw object</paramref>
/// and the GUID and name of the collection associated with this game object.</returns>
public (nint GameObject, (Guid Id, string Name) Collection) GetDrawObjectInfo(nint drawObject);
/// <summary>
/// Obtain the parent game object index for an unnamed cutscene actor by its <paramref name="actorIdx">index</paramref>.
/// </summary>
/// <param name="actorIdx"></param>
/// <returns>The parent game object index.</returns>
public int GetCutsceneParentIndex(int actorIdx);
/// <summary>
/// Set the cutscene parent of <paramref name="copyIdx"/> in Penumbras internal state to a new value.
/// </summary>
/// <param name="copyIdx"> The index of the cutscene actor to be changed. </param>
/// <param name="newParentIdx"> The new index of the cutscene actors parent or -1 for no parent. </param>
/// <returns> Success when the new parent could be set, or InvalidArgument if either index is out of its respective range. </returns>
/// <remarks>
/// Checks that the new parent exists as a game object if the value is not -1 before assigning. If it does not, InvalidArgument is given, too.
/// Please only use this for good reason and if you know what you are doing, probably only for actor copies you actually create yourself.
/// </remarks>
public PenumbraApiEc SetCutsceneParentIndex(int copyIdx, int newParentIdx);
/// <summary>
/// Triggered when a character base is created and a corresponding gameObject could be found,
/// before the Draw Object is actually created, so customize and equipdata can be manipulated beforehand.
/// </summary>
/// <returns><inheritdoc cref="CreatingCharacterBaseDelegate"/></returns>
public event CreatingCharacterBaseDelegate? CreatingCharacterBase;
/// <summary>
/// Triggered after a character base was created if a corresponding gameObject could be found,
/// so you can apply flag changes after finishing.
/// </summary>
/// <returns><inheritdoc cref="CreatedCharacterBaseDelegate"/></returns>
public event CreatedCharacterBaseDelegate? CreatedCharacterBase;
/// <summary>
/// Triggered whenever a resource is redirected by Penumbra for a specific, identified game object.
/// Does not trigger if the resource is not requested for a known game object.
/// </summary>
/// <returns><inheritdoc cref="GameObjectResourceResolvedDelegate"/></returns>
public event GameObjectResourceResolvedDelegate? GameObjectResourceResolved;
}

View File

@@ -0,0 +1,41 @@
namespace Penumbra.Api.Api;
/// <summary> The entire API. </summary>
public interface IPenumbraApi : IPenumbraApiBase
{
/// <inheritdoc cref="IPenumbraApiCollection"/>
public IPenumbraApiCollection Collection { get; }
/// <inheritdoc cref="IPenumbraApiEditing"/>
public IPenumbraApiEditing Editing { get; }
/// <inheritdoc cref="IPenumbraApiGameState"/>
public IPenumbraApiGameState GameState { get; }
/// <inheritdoc cref="IPenumbraApiMeta"/>
public IPenumbraApiMeta Meta { get; }
/// <inheritdoc cref="IPenumbraApiMods"/>
public IPenumbraApiMods Mods { get; }
/// <inheritdoc cref="IPenumbraApiModSettings"/>
public IPenumbraApiModSettings ModSettings { get; }
/// <inheritdoc cref="IPenumbraApiPluginState"/>
public IPenumbraApiPluginState PluginState { get; }
/// <inheritdoc cref="IPenumbraApiRedraw"/>
public IPenumbraApiRedraw Redraw { get; }
/// <inheritdoc cref="IPenumbraApiResolve"/>
public IPenumbraApiResolve Resolve { get; }
/// <inheritdoc cref="IPenumbraApiResourceTree"/>
public IPenumbraApiResourceTree ResourceTree { get; }
/// <inheritdoc cref="IPenumbraApiTemporary"/>
public IPenumbraApiTemporary Temporary { get; }
/// <inheritdoc cref="IPenumbraApiUi"/>
public IPenumbraApiUi Ui { get; }
}

View File

@@ -0,0 +1,16 @@
namespace Penumbra.Api.Api;
/// <summary> Base interface for the API that is always available, regardless of version. </summary>
public interface IPenumbraApiBase
{
/// <summary>
/// The API version is staggered in two parts.
/// The major/Breaking version only increments if there are changes breaking backwards compatibility.
/// The minor/Feature version increments any time there is something added
/// and resets when Breaking is incremented.
/// </summary>
public (int Breaking, int Feature) ApiVersion { get; }
/// <summary> Whether the API is still usable. </summary>
public bool Valid { get; }
}

13
Penumbra.Api/Api/Meta.cs Normal file
View File

@@ -0,0 +1,13 @@
namespace Penumbra.Api.Api;
/// <summary> API methods pertaining to current metadata manipulations. </summary>
public interface IPenumbraApiMeta
{
/// <returns>A base64 encoded, zipped json-string with a prepended version-byte of the current manipulations
/// in the collection currently associated with the player.</returns>
public string GetPlayerMetaManipulations();
/// <returns>A base64 encoded, zipped json-string with a prepended version-byte of the current manipulations
/// in the given collection applying to the given game object or the default collection if it does not exist.</returns>
public string GetMetaManipulations(int gameObjectIdx);
}

View File

@@ -0,0 +1,77 @@
using Penumbra.Api.Enums;
using Penumbra.Api.Helpers;
namespace Penumbra.Api.Api;
/// <summary> API methods pertaining to the management of mod settings. </summary>
public interface IPenumbraApiModSettings
{
/// <summary>
/// Obtain the potential settings of a mod given by its <paramref name="modDirectory" /> name or <paramref name="modName" />.
/// </summary>
/// <returns>A dictionary of group names to lists of option names and the group type. Null if the mod could not be found.</returns>
public AvailableModSettings? GetAvailableModSettings(string modDirectory, string modName);
/// <summary>
/// Obtain the enabled state, the priority, the settings of a mod given by its <paramref name="modDirectory" /> name or <paramref name="modName" /> in the specified collection.
/// </summary>
/// <param name="collectionId">Specify the collection.</param>
/// <param name="modDirectory">Specify the mod via its directory name.</param>
/// <param name="modName">Specify the mod via its (non-unique) display name.</param>
/// <param name="ignoreInheritance">Whether the settings need to be from the given collection or can be inherited from any other by it. (True: given collection only)</param>
/// <param name="ignoreTemporary"> Whether the settings need to be actual settings or can be temporary. </param>
/// <param name="key"> The key for the settings lock. If <paramref name="ignoreTemporary"/> is false, settings with a key greater than 0 that is different from this will be ignored. </param>
/// <returns>ModMissing, CollectionMissing or Success. <para />
/// On Success, a tuple of Enabled State, Priority, a dictionary of option group names and lists of enabled option names and a bool whether the settings are inherited (true) or not.</returns>
public (PenumbraApiEc, (bool, int, Dictionary<string, List<string>>, bool, bool)?) GetCurrentModSettingsWithTemp(Guid collectionId,
string modDirectory, string modName, bool ignoreInheritance, bool ignoreTemporary, int key);
/// <inheritdoc cref="GetCurrentModSettingsWithTemp"/>
public (PenumbraApiEc, (bool, int, Dictionary<string, List<string>>, bool)?) GetCurrentModSettings(Guid collectionId,
string modDirectory, string modName, bool ignoreInheritance);
/// <summary> Obtain the enabled state, the priority, the settings of all mods in the specified collection. </summary>
/// <param name="collectionId"> Specify the collection. </param>
/// <param name="ignoreInheritance"> Whether the settings need to be from the given collection or can be inherited from any other by it. (True: given collection only) </param>
/// <param name="ignoreTemporary"> Whether the settings need to be actual settings or can be temporary. </param>
/// <param name="key"> The key for the settings lock. If <paramref name="ignoreTemporary"/> is false, settings with a key greater than 0 that is different from this will be ignored. </param>
/// <returns> CollectionMissing or Success, on Success, a dictionary of mod directory names to a tuple of (Enabled, Priority, Settings, Inherited, Temporary). Mods that have no settings at all are left out. </returns>
public (PenumbraApiEc, Dictionary<string, (bool, int, Dictionary<string, List<string>>, bool, bool)>?) GetAllModSettings(Guid collectionId,
bool ignoreInheritance, bool ignoreTemporary, int key);
/// <summary> Try to set the inheritance state of a mod in a collection. </summary>
/// <returns>ModMissing, CollectionMissing, InvalidArgument (GUID is nil), NothingChanged or Success.</returns>
public PenumbraApiEc TryInheritMod(Guid collectionId, string modDirectory, string modName, bool inherit);
/// <summary> Try to set the enabled state of a mod in a collection. </summary>
/// <returns>ModMissing, CollectionMissing, InvalidArgument (GUID is nil), NothingChanged or Success.</returns>
public PenumbraApiEc TrySetMod(Guid collectionId, string modDirectory, string modName, bool enabled);
/// <summary> Try to set the priority of a mod in a collection. </summary>
/// <returns>ModMissing, CollectionMissing, InvalidArgument (GUID is nil), NothingChanged or Success.</returns>
public PenumbraApiEc TrySetModPriority(Guid collectionId, string modDirectory, string modName, int priority);
/// <summary> Try to set a specific option group of a mod in the given collection to a specific value. </summary>
/// <remarks>Removes inheritance. Single Selection groups should provide a single option, Multi Selection can provide multiple.
/// If any setting can not be found, it will not change anything.</remarks>
/// <returns>ModMissing, CollectionMissing, OptionGroupMissing, SettingMissing, InvalidArgument (GUID is nil), NothingChanged or Success.</returns>
public PenumbraApiEc TrySetModSetting(Guid collectionId, string modDirectory, string modName, string optionGroupName, string optionName);
/// <inheritdoc cref="TrySetModSetting"/>
public PenumbraApiEc TrySetModSettings(Guid collectionId, string modDirectory, string modName, string optionGroupName,
IReadOnlyList<string> optionNames);
/// <summary> This event gets fired when any setting in any collection changes. </summary>
/// <returns><inheritdoc cref="ModSettingChangedDelegate" /></returns>
public event ModSettingChangedDelegate? ModSettingChanged;
/// <summary>
/// Copy all current settings for a mod to another mod.
/// </summary>
/// <param name="collectionId">Specify the collection to work in, leave null to do it in all collections.</param>
/// <param name="modDirectoryFrom">Specify the mod to take the settings from via its directory name.</param>
/// <param name="modDirectoryTo">Specify the mod to put the settings on via its directory name. If the mod does not exist, it will be added as unused settings.</param>
/// <returns>CollectionMissing if collectionName is not empty but does not exist or Success.</returns>
/// <remarks>If the target mod exists, the settings will be fixed before being applied. If the source mod does not exist, it will use unused settings if available and remove existing settings otherwise.</remarks>
public PenumbraApiEc CopyModSettings(Guid? collectionId, string modDirectoryFrom, string modDirectoryTo);
}

78
Penumbra.Api/Api/Mods.cs Normal file
View File

@@ -0,0 +1,78 @@
using Penumbra.Api.Enums;
namespace Penumbra.Api.Api;
/// <summary> API methods pertaining to management of mods. </summary>
public interface IPenumbraApiMods
{
/// <returns>A list of all installed mods. The first string is their directory name, the second string is their mod name.</returns>
public Dictionary<string, string> GetModList();
/// <summary> Try to unpack and install a valid mod file (.pmp, .ttmp, .ttmp2) as if installed manually. </summary>
/// <param name="modFilePackagePath">The file that should be unpacked.</param>
/// <returns>Success, MissingFile. Success does not indicate successful installing, just successful queueing for install.</returns>
public PenumbraApiEc InstallMod(string modFilePackagePath);
/// <summary> Try to reload an existing mod given by its <paramref name="modDirectory" /> name or <paramref name="modName" />.</summary>
/// <remarks>Reload is the same as if triggered by button press and might delete the mod if it is not valid anymore.</remarks>
/// <returns>ModMissing if the mod can not be found or Success</returns>
public PenumbraApiEc ReloadMod(string modDirectory, string modName);
/// <summary> Try to add a new mod inside the mod root directory.</summary>
/// <remarks>Note that success does only imply a successful call, not a successful mod load.</remarks>
/// <param name="modDirectory">The name (not full name) of the mod directory.</param>
/// <returns>FileMissing if <paramref name="modDirectory" /> does not exist, InvalidArgument if the path leads outside the root directory, Success otherwise.</returns>
public PenumbraApiEc AddMod(string modDirectory);
/// <summary>Try to delete a mod given by its <paramref name="modDirectory" /> name or <paramref name="modName" />.</summary>
/// <remarks>Note that success does only imply a successful call, not successful deletion.</remarks>
/// <returns>NothingDone if the mod can not be found, Success otherwise.</returns>
public PenumbraApiEc DeleteMod(string modDirectory, string modName);
/// <summary> Triggers whenever a mod is deleted. </summary>
/// <returns>The base directory name of the deleted mod.</returns>
public event Action<string>? ModDeleted;
/// <summary> Triggers whenever a mod is deleted. </summary>
/// <returns>The base directory name of the new mod.</returns>
public event Action<string>? ModAdded;
/// <summary> Triggers whenever a mods base name is changed from inside Penumbra. </summary>
/// <returns>The previous base directory name of the mod and the new base directory name of the mod.</returns>
public event Action<string, string>? ModMoved;
/// <summary>
/// Get the internal full filesystem path including search order for the specified mod
/// given by its <paramref name="modDirectory" /> name or <paramref name="modName" />.
/// </summary>
/// <returns>On Success, the full path, a bool indicating whether the entire path is default (true) or manually set (false),
/// and a bool indicating whether the sort order name ignoring the folder path is default (true) or manually set (false).
/// Otherwise, returns ModMissing if the mod can not be found.</returns>
public (PenumbraApiEc, string, bool, bool) GetModPath(string modDirectory, string modName);
/// <summary>
/// Set the internal search order and filesystem path of the specified mod
/// given by its <paramref name="modDirectory" /> name or <paramref name="modName" />
/// to the <paramref name="newPath" />.
/// </summary>
/// <returns>InvalidArgument if <paramref name="newPath" /> is empty, ModMissing if the mod can not be found,
/// PathRenameFailed if <paramref name="newPath"/> could not be set or Success.</returns>
public PenumbraApiEc SetModPath(string modDirectory, string modName, string newPath);
/// <summary> Get the overall changed items of a single mod given by its <paramref name="modDirectory"/> name or <paramref name="modName"/>, regardless of settings. </summary>
/// <returns> A possibly empty dictionary of affected items and known objects or null. </returns>
public Dictionary<string, object?> GetChangedItems(string modDirectory, string modName);
/// <summary> Get a dictionary of dictionaries to check all mods changed items. </summary>
/// <returns> A dictionary of mod identifier to changed item dictionary. </returns>
/// <remarks> Throws an <seealso cref="ObjectDisposedException"/> on access if the mod storage is not valid anymore, so clear this on <seealso cref="IpcSubscribers.Disposed"/>. </remarks>
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, object?>> GetChangedItemAdapterDictionary();
/// <summary> Get a list of dictionaries to check all mods changed items. </summary>
/// <returns> A list all mods changed item dictionaries. </returns>
/// <remarks>
/// The order of mods is unspecified, but the same as in GetModList (assuming no changes in mods have taken place between calls). <br/>
/// Throws an <seealso cref="ObjectDisposedException"/> on access if the mod storage is not valid anymore, so clear this on <seealso cref="IpcSubscribers.Disposed"/>.
/// </remarks>
public IReadOnlyList<(string ModDirectory, IReadOnlyDictionary<string, object?> ChangedItems)> GetChangedItemAdapterList();
}

View File

@@ -0,0 +1,26 @@
namespace Penumbra.Api.Api;
/// <summary> API methods pertaining to Penumbras own state. </summary>
public interface IPenumbraApiPluginState
{
/// <returns> The full path of the current penumbra root directory. </returns>
public string GetModDirectory();
/// <returns> The entire current penumbra configuration as a json encoded string. </returns>
public string GetConfiguration();
/// <summary>
/// Fired whenever a mod directory change is finished.
/// </summary>
/// <returns>The full path of the mod directory and whether Penumbra treats it as valid.</returns>
public event Action<string, bool>? ModDirectoryChanged;
/// <returns>True if Penumbra is enabled, false otherwise.</returns>
public bool GetEnabledState();
/// <summary>
/// Fired whenever the enabled state of Penumbra changes.
/// </summary>
/// <returns>True if the new state is enabled, false if the new state is disabled</returns>
public event Action<bool>? EnabledChange;
}

View File

@@ -0,0 +1,23 @@
using Penumbra.Api.Enums;
namespace Penumbra.Api.Api;
/// <summary> API methods pertaining to the redrawing of actors. </summary>
public interface IPenumbraApiRedraw
{
/// <summary>
/// Queue redrawing of the actor with the given object <paramref name="gameObjectIndex" />, if it exists, with the given RedrawType <paramref name="setting"/>.
/// </summary>
public void RedrawObject(int gameObjectIndex, RedrawType setting);
/// <summary>
/// Queue redrawing of all currently available actors with the given RedrawType <paramref name="setting"/>.
/// </summary>
public void RedrawAll(RedrawType setting);
/// <summary>
/// Triggered whenever a game object is redrawn via Penumbra.
/// </summary>
/// /<returns><inheritdoc cref="GameObjectRedrawnDelegate"/></returns>
public event GameObjectRedrawnDelegate? GameObjectRedrawn;
}

View File

@@ -0,0 +1,64 @@
using Lumina.Data;
namespace Penumbra.Api.Api;
/// <summary> API methods pertaining to the resolving of paths. </summary>
public interface IPenumbraApiResolve
{
/// <summary>
/// Resolve a given <paramref name="gamePath" /> via Penumbra using the Base collection.
/// </summary>
/// <returns>The resolved path, or the given path if Penumbra would not manipulate it.</returns>
public string ResolveDefaultPath(string gamePath);
/// <summary>
/// Resolve a given <paramref name="gamePath" /> via Penumbra using the Interface collection.
/// </summary>
/// <returns>The resolved path, or the given path if Penumbra would not manipulate it.</returns>
public string ResolveInterfacePath(string gamePath);
/// <summary>
/// Resolve a given <paramref name="gamePath" /> via Penumbra using collection applying to the <paramref name="gameObjectIdx"/>
/// given by its index in the game object table.
/// </summary>
/// <remarks>If the object does not exist in the table, the default collection is used.</remarks>
/// <returns>The resolved path, or the given path if Penumbra would not manipulate it.</returns>
public string ResolveGameObjectPath(string gamePath, int gameObjectIdx);
/// <summary>
/// Resolve a given <paramref name="gamePath" /> via Penumbra using the collection currently applying to the player character.
/// </summary>
/// <returns>The resolved path, or the given path if Penumbra would not manipulate it.</returns>
public string ResolvePlayerPath(string gamePath);
/// <summary>
/// Reverse resolves a given local <paramref name="moddedPath" /> into its replacement in form of all applicable game paths
/// for the collection applying to the <paramref name="gameObjectIdx"/>th game object in the game object table.
/// </summary>
/// <remarks>If the object does not exist in the table, the default collection is used.</remarks>
/// <returns>A list of game paths resolving to the modded path.</returns>
public string[] ReverseResolveGameObjectPath(string moddedPath, int gameObjectIdx);
/// <summary>
/// Reverse resolves a given local <paramref name="moddedPath" /> into its replacement in form of all applicable game paths
/// for the collection currently applying to the player character.
/// </summary>
/// <returns>A list of game paths resolving to the modded path.</returns>
public string[] ReverseResolvePlayerPath(string moddedPath);
/// <summary>
/// Resolve all game paths in <paramref name="forward"/> and reserve all paths in <paramref name="reverse"/> at once.
/// </summary>
/// <param name="forward">Paths to forward-resolve.</param>
/// <param name="reverse">Paths to reverse-resolve.</param>
/// <returns>A pair of an array of forward-resolved single paths of the same length as <paramref name="forward"/> and an array of arrays of reverse-resolved paths.
/// The outer array has the same length as <paramref name="reverse"/> while each inner array can have arbitrary length.</returns>
public (string[], string[][]) ResolvePlayerPaths(string[] forward, string[] reverse);
/// <summary>
/// Resolve all game paths in <paramref name="forward"/> and reserve all paths in <paramref name="reverse"/> at once asynchronously.
/// </summary>
/// <inheritdoc cref="ResolvePlayerPaths"/>
/// <remarks> Can be called from outside of framework. Can theoretically produce incoherent state when collections change during evaluation. </remarks>
public Task<(string[], string[][])> ResolvePlayerPathsAsync(string[] forward, string[] reverse);
}

View File

@@ -0,0 +1,73 @@
using Newtonsoft.Json.Linq;
using Penumbra.Api.Enums;
using Penumbra.Api.Helpers;
namespace Penumbra.Api.Api;
/// <summary> API methods pertaining to the tracking of resources in use by actors. </summary>
public interface IPenumbraApiResourceTree
{
/// <summary>
/// Get the given game objects' resources, as dictionaries of actual paths (that may be FS paths for redirected resources, or game paths for swapped or vanilla resources) to game paths.
/// </summary>
/// <param name="gameObjects"> The game object indices for which to get the resources. </param>
/// <returns> An array of resource path dictionaries, of the same length and in the same order as the given game object index array. </returns>
/// <remarks> This function is best called right after the game objects are redrawn, as it may fail to resolve paths if relevant mod settings have changed since then. </remarks>
public Dictionary<string, HashSet<string>>?[] GetGameObjectResourcePaths(params ushort[] gameObjects);
/// <summary>
/// Get the player and player-owned game objects' resources, as dictionaries of actual paths (that may be FS paths for redirected resources, or game paths for swapped or vanilla resources) to game paths.
/// </summary>
/// <returns> A dictionary of game object indices to resource path dictionaries. </returns>
/// <remarks> This function is best called right after the game objects are redrawn, as it may fail to resolve paths if relevant mod settings have changed since then. </remarks>
public Dictionary<ushort, Dictionary<string, HashSet<string>>> GetPlayerResourcePaths();
/// <summary>
/// Get the given game objects' resources of a given type, as dictionaries of resource handles to actual paths and, optionally, names and icons.
/// </summary>
/// <param name="type"> Type of the resources to get, for example <see cref="ResourceType.Mtrl"/> for materials. </param>
/// <param name="withUiData"> Whether to get names and icons along with the paths. </param>
/// <param name="gameObjects"> The game object indices for which to get the resources. </param>
/// <returns> An array of resource information dictionaries, of the same length and in the same order as the given game object index array. </returns>
/// <remarks>
/// It is the caller's responsibility to make sure the returned resource handles are still in use on the game object's draw object before using them. <para />
/// Also, callers should not use UI data for non-UI purposes.
/// </remarks>
public GameResourceDict?[] GetGameObjectResourcesOfType(ResourceType type, bool withUiData,
params ushort[] gameObjects);
/// <summary>
/// Get the player and player-owned game objects' resources of a given type, as dictionaries of resource handles to actual paths and, optionally, names and icons.
/// </summary>
/// <param name="type"> Type of the resources to get, for example <see cref="ResourceType.Mtrl"/> for materials. </param>
/// <param name="withUiData"> Whether to get names and icons along with the paths. </param>
/// <returns> A dictionary of game object indices to resource information dictionaries. </returns>
/// <remarks>
/// It is the caller's responsibility to make sure the returned resource handles are still in use on the game object's draw object before using them. <para />
/// Also, callers should not use UI data for non-UI purposes.
/// </remarks>
public Dictionary<ushort, GameResourceDict> GetPlayerResourcesOfType(ResourceType type, bool withUiData);
/// <summary>
/// Get the given game objects' resource tree.
/// </summary>
/// <param name="withUiData"> Whether to get names and icons along with the paths. </param>
/// <param name="gameObjects"> The game object indices for which to get the resources. </param>
/// <returns> An array of resource tree JObjects, of the same length and in the same order as the given game object index array. </returns>
/// <remarks>
/// It is the caller's responsibility to make sure the returned resource handles are still in use on the game object's draw object before using them. <para />
/// Also, callers should not use UI data for non-UI purposes.
/// </remarks>
public JObject?[] GetGameObjectResourceTrees(bool withUiData, params ushort[] gameObjects);
/// <summary>
/// Get the player and player-owned game objects' resource trees.
/// </summary>
/// <param name="withUiData"> Whether to get names and icons along with the paths. </param>
/// <returns> A dictionary of game object indices to resource trees. </returns>
/// <remarks>
/// It is the caller's responsibility to make sure the returned resource handles are still in use on the game object's draw object before using them. <para />
/// Also, callers should not use UI data for non-UI purposes.
/// </remarks>
public Dictionary<ushort, JObject> GetPlayerResourceTrees(bool withUiData);
}

View File

@@ -0,0 +1,147 @@
using Penumbra.Api.Enums;
namespace Penumbra.Api.Api;
/// <summary> API methods pertaining to the management of temporary collections and mods. </summary>
public interface IPenumbraApiTemporary
{
/// <summary> Temporarily set the settings of a mod in a collection to given values. </summary>
/// <param name="collectionId"> The collection to manipulate. </param>
/// <param name="modDirectory"> Specify the mod via its directory name. </param>
/// <param name="modName"> Specify the mod via its (non-unique) display name. </param>
/// <param name="inherit"> Whether the mod should be forced to inherit from parent collections (if this is true, the other settings do not matter). </param>
/// <param name="enabled"> Whether the mod should be enabled or disabled. </param>
/// <param name="priority"> The desired priority for the mod. </param>
/// <param name="options"> The new settings for the mod, as a map of Group Name -> All enabled Options (should be only one for single select groups).</param>
/// <param name="source"> A string to describe the source of those temporary settings. This is displayed to the user. </param>
/// <param name="key"> An optional lock to prevent other plugins and the user from changing these settings. Changes in mod structure will still remove the settings. Use 0 for no lock, or negative numbers for an identification lock that does not prevent the user from editing the temporary settings, but allows you to use <seealso cref="RemoveAllTemporaryModSettings"/> with the same key to only remove your settings. </param>
/// <returns> Success, CollectionMissing if the collection does not exist, TemporarySettingImpossible if the collection can not have settings, ModMissing if the mod can not be identified, TemporarySettingDisallowed if there is already a temporary setting with a different key, OptionGroupMissing if a group can not be found, OptionMissing if an option can not be found. </returns>
/// <remarks> If not all groups are set in <paramref name="options"/>, they will be set to their default settings. </remarks>
public PenumbraApiEc SetTemporaryModSettings(Guid collectionId, string modDirectory, string modName, bool inherit, bool enabled, int priority,
IReadOnlyDictionary<string, IReadOnlyList<string>> options, string source, int key);
/// <summary> Temporarily set the settings of a mod in a collection to given values. </summary>
/// <param name="objectIndex"> The game object index of the object whose collection you want to change. </param>
/// <param name="modDirectory"> Specify the mod via its directory name. </param>
/// <param name="modName"> Specify the mod via its (non-unique) display name. </param>
/// <param name="inherit"> Whether the mod should be forced to inherit from parent collections (if this is true, the other settings do not matter). </param>
/// <param name="enabled"> Whether the mod should be enabled or disabled. </param>
/// <param name="priority"> The desired priority for the mod. </param>
/// <param name="options"> The new settings for the mod, as a map of Group Name -> All enabled Options (should be only one for single select groups).</param>
/// <param name="source"> A string to describe the source of those temporary settings. This is displayed to the user. </param>
/// <param name="key"> An optional lock to prevent other plugins and the user from changing these settings. Changes in mod structure will still remove the settings. Use 0 for no lock. </param>
/// <returns> Success, InvalidArgument if the game object does not exist, TemporarySettingImpossible if the collection can not have settings, ModMissing if the mod can not be identified, TemporarySettingDisallowed if there is already a temporary setting with a different key, OptionGroupMissing if a group can not be found, OptionMissing if an option can not be found. </returns>
/// <remarks> If not all groups are set in <paramref name="options"/>, they will be set to their default settings. </remarks>
public PenumbraApiEc SetTemporaryModSettingsPlayer(int objectIndex, string modDirectory, string modName, bool inherit, bool enabled, int priority,
IReadOnlyDictionary<string, IReadOnlyList<string>> options, string source, int key);
/// <summary> Temporarily set the settings of a mod in a collection to given values. </summary>
/// <param name="collectionId"> The collection to manipulate. </param>
/// <param name="modDirectory"> Specify the mod via its directory name. </param>
/// <param name="modName"> Specify the mod via its (non-unique) display name. </param>
/// <param name="key"> An optional key to a potential lock applied to those settings. </param>
/// <returns> Success, NothingDone if no temporary settings could be removed with this key, CollectionMissing if the collection does not exist, TemporarySettingDisallowed if the key did not correspond to the lock. </returns>
public PenumbraApiEc RemoveTemporaryModSettings(Guid collectionId, string modDirectory, string modName, int key);
/// <summary> Temporarily set the settings of a mod in a collection to given values. </summary>
/// <param name="objectIndex"> The game object index of the object whose collection you want to change. </param>
/// <param name="modDirectory"> Specify the mod via its directory name. </param>
/// <param name="modName"> Specify the mod via its (non-unique) display name. </param>
/// <param name="key"> An optional key to a potential lock applied to those settings. </param>
/// <returns> Success, NothingDone if the mod did not have temporary settings in this collection, InvalidArgument if the game object does not exist, TemporarySettingDisallowed if the key did not correspond to the lock. </returns>
public PenumbraApiEc RemoveTemporaryModSettingsPlayer(int objectIndex, string modDirectory, string modName, int key);
/// <summary> Temporarily set the settings of a mod in a collection to given values. </summary>
/// <param name="collectionId"> The collection to manipulate. </param>
/// <param name="key"> An optional key to a lock applied to those settings. All settings that use this key will be removed, all others ignored. </param>
/// <returns> Success, NothingDone if no temporary settings could be removed with this key, CollectionMissing if the collection does not exist. </returns>
public PenumbraApiEc RemoveAllTemporaryModSettings(Guid collectionId, int key);
/// <summary> Temporarily set the settings of a mod in a collection to given values. </summary>
/// <param name="objectIndex"> The game object index of the object whose collection you want to change. </param>
/// <param name="key"> An optional key to a lock applied to those settings. All settings that can be removed with this key will be removed, all others ignored. </param>
/// <returns> Success, NothingDone if no temporary settings could be removed with this key, InvalidArgument if the game object does not exist. </returns>
public PenumbraApiEc RemoveAllTemporaryModSettingsPlayer(int objectIndex, int key);
/// <summary> Create a temporary collection. </summary>
/// <param name="name"> The name for the collection. Arbitrary and only used internally for debugging. </param>
/// <returns> The GUID of the created temporary collection. </returns>
public Guid CreateTemporaryCollection(string name);
/// <summary> Remove the temporary collection of the given name. </summary>
/// <param name="collectionId"> The chosen temporary collection to remove. </param>
/// <returns> NothingChanged or Success. </returns>
public PenumbraApiEc DeleteTemporaryCollection(Guid collectionId);
/// <summary>
/// Assign an existing temporary collection to an actor that currently occupies a specific slot.
/// </summary>
/// <param name="collectionId">The chosen collection assigned to the actor.</param>
/// <param name="actorIndex">The current object table index of the actor.</param>
/// <param name="forceAssignment">Whether to assign even if the actor is already assigned either a temporary or a permanent collection.</param>
/// <returns>Success, InvalidArgument if the actor can not be identified, CollectionMissing if the collection does not exist, CharacterCollectionExists if <paramref name="forceAssignment"/> is false and the actor is already assigned a collection, and AssignmentDeletionFailed if <paramref name="forceAssignment"/> is true and the existing temporary assignment could not be deleted. </returns>
public PenumbraApiEc AssignTemporaryCollection(Guid collectionId, int actorIndex, bool forceAssignment);
/// <summary>
/// Set a temporary mod with the given paths, manipulations and priority and the name tag to all regular and temporary collections.
/// </summary>
/// <param name="tag">Custom name for the temporary mod.</param>
/// <param name="paths">List of redirections (can be swaps or redirections).</param>
/// <param name="manipString">Zipped Base64 string of meta manipulations.</param>
/// <param name="priority">Desired priority.</param>
/// <returns>InvalidGamePath, InvalidManipulation or Success.</returns>
public PenumbraApiEc AddTemporaryModAll(string tag, Dictionary<string, string> paths, string manipString, int priority);
/// <summary> Set a temporary mod with the given paths, manipulations and priority and the name tag to a specific collection.
/// </summary>
/// <param name="tag">Custom name for the temporary mod.</param>
/// <param name="collectionId">GUID of the collection the mod should apply to. Can be a temporary collection.</param>
/// <param name="paths">List of redirections (can be swaps or redirections).</param>
/// <param name="manipString">Zipped Base64 string of meta manipulations.</param>
/// <param name="priority">Desired priority.</param>
/// <returns>CollectionMissing, InvalidGamePath, InvalidManipulation, InvalidArgument (GUID is nil) or Success.</returns>
public PenumbraApiEc AddTemporaryMod(string tag, Guid collectionId, Dictionary<string, string> paths, string manipString,
int priority);
/// <summary>
/// Remove the temporary mod with the given tag and priority from the temporary mods applying to all collections, if it exists.
/// </summary>
/// <param name="tag">The tag to look for.</param>
/// <param name="priority">The initially provided priority.</param>
/// <returns>NothingDone or Success.</returns>
public PenumbraApiEc RemoveTemporaryModAll(string tag, int priority);
/// <summary>
/// Remove the temporary mod with the given tag and priority from the temporary mods applying to a specific collection, if it exists.
/// </summary>
/// <param name="tag">The tag to look for.</param>
/// <param name="collectionId">GUID of the collection the mod should apply to. Can be a temporary collection.</param>
/// <param name="priority">The initially provided priority.</param>
/// <returns>CollectionMissing, NothingDone or Success.</returns>
public PenumbraApiEc RemoveTemporaryMod(string tag, Guid collectionId, int priority);
/// <summary> Get the current temporary settings of a mod in the given collection. </summary>
/// <param name="collectionId"> The collection to query. </param>
/// <param name="modDirectory"> Specify the mod via its directory name. </param>
/// <param name="modName"> Specify the mod via its (non-unique) display name. </param>
/// <param name="key"> The key for the settings lock.</param>
/// <returns>
/// The settings as (ForceInherit, Enabled, Priority, Settings) or null if none are registered,
/// the registered source for the temporary settings or empty,
/// and Success, CollectionMissing, ModMissing or TemporarySettingDisallowed if the used key was > 0 and different from the provided key.
/// </returns>
public (PenumbraApiEc ErrorCode, (bool, bool, int, Dictionary<string, List<string>>)?, string) QueryTemporaryModSettings(Guid collectionId, string modDirectory,
string modName, int key);
/// <summary> Get the current temporary settings of a mod in the collection assigned to a given game object. </summary>
/// <param name="objectIndex"> The game object index of the object whose collection you want to change. </param>
/// <param name="modDirectory"> Specify the mod via its directory name. </param>
/// <param name="modName"> Specify the mod via its (non-unique) display name. </param>
/// <param name="key"> The key for the settings lock.</param>
/// <returns>
/// The settings as (ForceInherit, Enabled, Priority, Settings) or null if none are registered,
/// the registered source for the temporary settings or empty,
/// and Success, InvalidArgument if the game object does not exist, ModMissing, or TemporarySettingDisallowed if the used key was > 0 and different from the provided key.
/// </returns>
public (PenumbraApiEc ErrorCode, (bool, bool, int, Dictionary<string, List<string>>)? Settings, string Source) QueryTemporaryModSettingsPlayer(int objectIndex, string modDirectory, string modName, int key);
}

59
Penumbra.Api/Api/Ui.cs Normal file
View File

@@ -0,0 +1,59 @@
using Penumbra.Api.Enums;
namespace Penumbra.Api.Api;
/// <summary> API methods pertaining to Penumbras UI. </summary>
public interface IPenumbraApiUi
{
/// <summary>
/// Triggered when the user hovers over a listed changed object in a mod tab.<para />
/// Can be used to append tooltips.
/// </summary>
/// <returns> The type of the changed item and its ID if known. </returns>
public event Action<ChangedItemType, uint>? ChangedItemTooltip;
/// <summary>
/// Triggered when the user clicks a listed changed object in a mod tab.
/// </summary>
/// <returns> The mouse button clicked, the type of the changed item and its ID if known. </returns>
public event Action<MouseButton, ChangedItemType, uint>? ChangedItemClicked;
/// <summary>
/// Triggered before the settings tab bar for a mod is drawn, after the title group is drawn.
/// </summary>
/// <returns>The directory name of the currently selected mod, the total used width of the title bar and the width of the title box.</returns>
public event Action<string, float, float>? PreSettingsTabBarDraw;
/// <summary>
/// Triggered before the content of a mod settings panel is drawn.
/// </summary>
/// <returns>The directory name of the currently selected mod.</returns>
public event Action<string>? PreSettingsPanelDraw;
/// <summary>
/// Triggered after the Enabled Checkbox line in settings is drawn, but before options are drawn.
/// </summary>
/// <returns>The directory name of the currently selected mod.</returns>
public event Action<string>? PostEnabledDraw;
/// <summary>
/// Triggered after the content of a mod settings panel is drawn, but still in the child window.
/// </summary>
/// <returns>The directory name of the currently selected mod.</returns>
public event Action<string>? PostSettingsPanelDraw;
/// <summary>
/// Open the Penumbra main config window.
/// </summary>
/// <param name="tab">Open the window at a specific tab. Use TabType.None to not change the tab. </param>
/// <param name="modDirectory">Select a mod specified via its directory name in the mod tab, empty if none.</param>
/// <param name="modName">Select a mod specified via its mod name in the mod tab, empty if none.</param>
/// <returns>InvalidArgument if <paramref name="tab"/> is invalid,
/// ModMissing if <paramref name="modDirectory"/> or <paramref name="modName"/> are set non-empty and the mod does not exist,
/// Success otherwise.</returns>
/// <remarks>If <paramref name="tab"/> is not TabType.Mods, the mod will not be selected regardless of other parameters and ModMissing will not be returned.</remarks>
public PenumbraApiEc OpenMainWindow(TabType tab, string modDirectory, string modName);
/// <summary> Close the Penumbra main config window. </summary>
public void CloseMainWindow();
}