Merge remote-tracking branch 'upstream/master' into flytext

This commit is contained in:
liam 2021-08-10 15:16:44 -04:00
commit 5812b5a9c0
110 changed files with 2605 additions and 1389 deletions

View file

@ -33,16 +33,23 @@
"GitHubActions",
"GitLab",
"Jenkins",
"Rider",
"SpaceAutomation",
"TeamCity",
"Terminal",
"TravisCI"
"TravisCI",
"VisualStudio",
"VSCode"
]
},
"NoLogo": {
"type": "boolean",
"description": "Disables displaying the NUKE logo"
},
"Partition": {
"type": "string",
"description": "Partition to use on CI"
},
"Plan": {
"type": "boolean",
"description": "Shows the execution plan (HTML)"

View file

@ -0,0 +1,27 @@
using System;
namespace Dalamud
{
/// <summary>
/// Extension methods for the <see cref="ClientLanguage"/> class.
/// </summary>
public static class ClientLanguageExtensions
{
/// <summary>
/// Converts a Dalamud ClientLanguage to the corresponding Lumina variant.
/// </summary>
/// <param name="language">Langauge to convert.</param>
/// <returns>Converted langauge.</returns>
public static Lumina.Data.Language ToLumina(this ClientLanguage language)
{
return language switch
{
ClientLanguage.Japanese => Lumina.Data.Language.Japanese,
ClientLanguage.English => Lumina.Data.Language.English,
ClientLanguage.German => Lumina.Data.Language.German,
ClientLanguage.French => Lumina.Data.Language.French,
_ => throw new ArgumentOutOfRangeException(nameof(language)),
};
}
}
}

View file

@ -92,6 +92,13 @@ namespace Dalamud.Configuration.Internal
/// </summary>
public Dictionary<string, DevPluginSettings> DevPluginSettings { get; set; } = new();
/// <summary>
/// Gets or sets a list of additional locations that dev plugins should be loaded from. This can
/// be either a DLL or folder, but should be the absolute path, or a path relative to the currently
/// injected Dalamud instance.
/// </summary>
public List<DevPluginLocationSettings> DevPluginLoadLocations { get; set; } = new();
/// <summary>
/// Gets or sets the global UI scale.
/// </summary>

View file

@ -0,0 +1,24 @@
namespace Dalamud.Configuration
{
/// <summary>
/// Additional locations to load dev plugins from.
/// </summary>
internal sealed class DevPluginLocationSettings
{
/// <summary>
/// Gets or sets the dev pluign path.
/// </summary>
public string Path { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the third party repo is enabled.
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// Clone this object.
/// </summary>
/// <returns>A shallow copy of this object.</returns>
public DevPluginLocationSettings Clone() => this.MemberwiseClone() as DevPluginLocationSettings;
}
}

View file

@ -7,11 +7,9 @@ using System.Threading;
using Dalamud.Configuration.Internal;
using Dalamud.Data;
using Dalamud.Game;
using Dalamud.Game.Addon;
using Dalamud.Game.ClientState;
using Dalamud.Game.Command;
using Dalamud.Game.Internal;
using Dalamud.Game.Network;
using Dalamud.Game.Network.Internal;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Hooking.Internal;

View file

@ -5,9 +5,8 @@ using System.Diagnostics;
using System.IO;
using System.Threading;
using Dalamud.Data.LuminaExtensions;
using Dalamud.Interface;
using Dalamud.Interface.Internal;
using Dalamud.Utility;
using ImGuiScene;
using JetBrains.Annotations;
using Lumina;
@ -96,15 +95,7 @@ namespace Dalamud.Data
/// <returns>The <see cref="ExcelSheet{T}"/>, giving access to game rows.</returns>
public ExcelSheet<T> GetExcelSheet<T>(ClientLanguage language) where T : ExcelRow
{
var lang = language switch
{
ClientLanguage.Japanese => Lumina.Data.Language.Japanese,
ClientLanguage.English => Lumina.Data.Language.English,
ClientLanguage.German => Lumina.Data.Language.German,
ClientLanguage.French => Lumina.Data.Language.French,
_ => throw new ArgumentOutOfRangeException(nameof(language), $"Unknown Language: {language}"),
};
return this.Excel.GetSheet<T>(lang);
return this.Excel.GetSheet<T>(language.ToLumina());
}
/// <summary>
@ -146,18 +137,30 @@ namespace Dalamud.Data
/// </summary>
/// <param name="iconId">The icon ID.</param>
/// <returns>The <see cref="TexFile"/> containing the icon.</returns>
public TexFile GetIcon(int iconId)
public TexFile GetIcon(uint iconId)
{
return this.GetIcon(this.Language, iconId);
}
/// <summary>
/// Get a <see cref="TexFile"/> containing the icon with the given ID, of the given quality.
/// </summary>
/// <param name="isHq">A value indicating whether the icon should be HQ.</param>
/// <param name="iconId">The icon ID.</param>
/// <returns>The <see cref="TexFile"/> containing the icon.</returns>
public TexFile GetIcon(bool isHq, uint iconId)
{
var type = isHq ? "hq/" : string.Empty;
return this.GetIcon(type, iconId);
}
/// <summary>
/// Get a <see cref="TexFile"/> containing the icon with the given ID, of the given language.
/// </summary>
/// <param name="iconLanguage">The requested language.</param>
/// <param name="iconId">The icon ID.</param>
/// <returns>The <see cref="TexFile"/> containing the icon.</returns>
public TexFile GetIcon(ClientLanguage iconLanguage, int iconId)
public TexFile GetIcon(ClientLanguage iconLanguage, uint iconId)
{
var type = iconLanguage switch
{
@ -177,7 +180,7 @@ namespace Dalamud.Data
/// <param name="type">The type of the icon (e.g. 'hq' to get the HQ variant of an item icon).</param>
/// <param name="iconId">The icon ID.</param>
/// <returns>The <see cref="TexFile"/> containing the icon.</returns>
public TexFile GetIcon(string type, int iconId)
public TexFile GetIcon(string type, uint iconId)
{
type ??= string.Empty;
if (type.Length > 0 && !type.EndsWith("/"))
@ -186,7 +189,8 @@ namespace Dalamud.Data
var filePath = string.Format(IconFileFormat, iconId / 1000, type, iconId);
var file = this.GetFile<TexFile>(filePath);
if (file != default(TexFile) || type.Length <= 0) return file;
if (type == string.Empty || file != default)
return file;
// Couldn't get specific type, try for generic version.
filePath = string.Format(IconFileFormat, iconId / 1000, string.Empty, iconId);
@ -194,6 +198,14 @@ namespace Dalamud.Data
return file;
}
/// <summary>
/// Get a <see cref="TexFile"/> containing the HQ icon with the given ID.
/// </summary>
/// <param name="iconId">The icon ID.</param>
/// <returns>The <see cref="TexFile"/> containing the icon.</returns>
public TexFile GetHqIcon(uint iconId)
=> this.GetIcon(true, iconId);
/// <summary>
/// Get the passed <see cref="TexFile"/> as a drawable ImGui TextureWrap.
/// </summary>
@ -210,13 +222,30 @@ namespace Dalamud.Data
public TextureWrap GetImGuiTexture(string path)
=> this.GetImGuiTexture(this.GetFile<TexFile>(path));
/// <summary>
/// Get a <see cref="TextureWrap"/> containing the icon with the given ID.
/// </summary>
/// <param name="iconId">The icon ID.</param>
/// <returns>The <see cref="TextureWrap"/> containing the icon.</returns>
public TextureWrap GetImGuiTextureIcon(uint iconId)
=> this.GetImGuiTexture(this.GetIcon(iconId));
/// <summary>
/// Get a <see cref="TextureWrap"/> containing the icon with the given ID, of the given quality.
/// </summary>
/// <param name="isHq">A value indicating whether the icon should be HQ.</param>
/// <param name="iconId">The icon ID.</param>
/// <returns>The <see cref="TextureWrap"/> containing the icon.</returns>
public TextureWrap GetImGuiTextureIcon(bool isHq, uint iconId)
=> this.GetImGuiTexture(this.GetIcon(isHq, iconId));
/// <summary>
/// Get a <see cref="TextureWrap"/> containing the icon with the given ID, of the given language.
/// </summary>
/// <param name="iconLanguage">The requested language.</param>
/// <param name="iconId">The icon ID.</param>
/// <returns>The <see cref="TextureWrap"/> containing the icon.</returns>
public TextureWrap GetImGuiTextureIcon(ClientLanguage iconLanguage, int iconId)
public TextureWrap GetImGuiTextureIcon(ClientLanguage iconLanguage, uint iconId)
=> this.GetImGuiTexture(this.GetIcon(iconLanguage, iconId));
/// <summary>
@ -225,9 +254,17 @@ namespace Dalamud.Data
/// <param name="type">The type of the icon (e.g. 'hq' to get the HQ variant of an item icon).</param>
/// <param name="iconId">The icon ID.</param>
/// <returns>The <see cref="TextureWrap"/> containing the icon.</returns>
public TextureWrap GetImGuiTextureIcon(string type, int iconId)
public TextureWrap GetImGuiTextureIcon(string type, uint iconId)
=> this.GetImGuiTexture(this.GetIcon(type, iconId));
/// <summary>
/// Get a <see cref="TextureWrap"/> containing the HQ icon with the given ID.
/// </summary>
/// <param name="iconId">The icon ID.</param>
/// <returns>The <see cref="TextureWrap"/> containing the icon.</returns>
public TextureWrap GetImGuiTextureHqIcon(uint iconId)
=> this.GetImGuiTexture(this.GetHqIcon(iconId));
#endregion
/// <summary>
@ -263,21 +300,12 @@ namespace Dalamud.Data
var luminaOptions = new LuminaOptions
{
CacheFileResources = true,
#if DEBUG
PanicOnSheetChecksumMismatch = true,
#else
PanicOnSheetChecksumMismatch = false,
#endif
DefaultExcelLanguage = this.Language switch
{
ClientLanguage.Japanese => Lumina.Data.Language.Japanese,
ClientLanguage.English => Lumina.Data.Language.English,
ClientLanguage.German => Lumina.Data.Language.German,
ClientLanguage.French => Lumina.Data.Language.French,
_ => throw new ArgumentOutOfRangeException(nameof(this.Language), $"Unknown Language: {this.Language}"),
},
DefaultExcelLanguage = this.Language.ToLumina(),
};
var processModule = Process.GetCurrentProcess().MainModule;

View file

@ -142,7 +142,7 @@ namespace Dalamud
var oldFile = new FileInfo(oldPath);
if (!oldFile.Exists)
oldFile.Create();
oldFile.Create().Close();
using var reader = new BinaryReader(logFile.Open(FileMode.Open, FileAccess.Read));
using var writer = new BinaryWriter(oldFile.Open(FileMode.Append, FileAccess.Write));

View file

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
namespace Dalamud.Game.Internal
namespace Dalamud.Game
{
/// <summary>
/// Base memory address resolver.
@ -11,9 +11,9 @@ namespace Dalamud.Game.Internal
public abstract class BaseAddressResolver
{
/// <summary>
/// A list of memory addresses that were found, to list in /xldata.
/// Gets a list of memory addresses that were found, to list in /xldata.
/// </summary>
public static Dictionary<string, List<(string, IntPtr)>> DebugScannedValues = new();
public static Dictionary<string, List<(string ClassName, IntPtr Address)>> DebugScannedValues { get; } = new();
/// <summary>
/// Gets or sets a value indicating whether the resolver has successfully run <see cref="Setup32Bit(SigScanner)"/> or <see cref="Setup64Bit(SigScanner)"/>.

View file

@ -65,7 +65,7 @@ namespace Dalamud.Game.ClientState.Actors.Types
/// <summary>
/// Gets the ClassJob of this Chara.
/// </summary>
public ClassJobResolver ClassJob => new(*(byte*)(this.Address + ActorOffsets.ClassJob), this.Dalamud);
public ExcelResolver<Lumina.Excel.GeneratedSheets.ClassJob> ClassJob => new(*(byte*)(this.Address + ActorOffsets.ClassJob), this.Dalamud);
/// <summary>
/// Gets the level of this Chara.

View file

@ -23,14 +23,14 @@ namespace Dalamud.Game.ClientState.Actors.Types
}
/// <summary>
/// Gets the current <see cref="WorldResolver">world</see> of the character.
/// Gets the current <see cref="ExcelResolver{T}">world</see> of the character.
/// </summary>
public WorldResolver CurrentWorld => new(*(ushort*)(this.Address + ActorOffsets.CurrentWorld), this.Dalamud);
public ExcelResolver<Lumina.Excel.GeneratedSheets.World> CurrentWorld => new(*(ushort*)(this.Address + ActorOffsets.CurrentWorld), this.Dalamud);
/// <summary>
/// Gets the home <see cref="WorldResolver">world</see> of the character.
/// Gets the home <see cref="ExcelResolver{T}">world</see> of the character.
/// </summary>
public WorldResolver HomeWorld => new(*(ushort*)(this.Address + ActorOffsets.HomeWorld), this.Dalamud);
public ExcelResolver<Lumina.Excel.GeneratedSheets.World> HomeWorld => new(*(ushort*)(this.Address + ActorOffsets.HomeWorld), this.Dalamud);
/// <summary>
/// Gets the Free Company tag of this player.

View file

@ -1,14 +1,16 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Actors;
using Dalamud.Game.ClientState.Actors.Types;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Fates;
using Dalamud.Game.Internal;
using Dalamud.Game.ClientState.GamePad;
using Dalamud.Game.ClientState.Keys;
using Dalamud.Hooking;
using JetBrains.Annotations;
using Lumina.Excel.GeneratedSheets;
using Serilog;
namespace Dalamud.Game.ClientState
@ -93,7 +95,7 @@ namespace Dalamud.Game.ClientState
/// <summary>
/// Event that gets fired when a duty is ready.
/// </summary>
public event EventHandler<ContentFinderCondition> CfPop;
public event EventHandler<Lumina.Excel.GeneratedSheets.ContentFinderCondition> CfPop;
/// <summary>
/// Gets the table of all present actors.
@ -194,7 +196,7 @@ namespace Dalamud.Game.ClientState
return this.setupTerritoryTypeHook.Original(manager, terriType);
}
private void NetworkHandlersOnCfPop(object sender, ContentFinderCondition e)
private void NetworkHandlersOnCfPop(object sender, Lumina.Excel.GeneratedSheets.ContentFinderCondition e)
{
this.CfPop?.Invoke(this, e);
}

View file

@ -1,6 +1,6 @@
using System;
namespace Dalamud.Game.ClientState
namespace Dalamud.Game.ClientState.Conditions
{
/// <summary>
/// Provides access to conditions (generally player state). You can check whether a player is in combat, mounted, etc.
@ -37,7 +37,7 @@ namespace Dalamud.Game.ClientState
{
var idx = (int)flag;
if (idx > MaxConditionEntries || idx < 0)
if (idx < 0 || idx >= MaxConditionEntries)
return false;
return *(bool*)(this.ConditionArrayBase + idx);

View file

@ -1,4 +1,4 @@
namespace Dalamud.Game.ClientState
namespace Dalamud.Game.ClientState.Conditions
{
/// <summary>
/// Possible state flags (or conditions as they're called internally) that can be set on the local client.

View file

@ -1,17 +1,19 @@
using System;
using System.Numerics;
using Dalamud.Game.ClientState.Resolvers;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Memory;
using FFXIVClientStructs.FFXIV.Client.System.String;
namespace Dalamud.Game.ClientState.Fates.Types
namespace Dalamud.Game.ClientState.Fates
{
/// <summary>
/// This class represents an FFXIV Fate.
/// </summary>
public unsafe partial class Fate : IEquatable<Fate>
{
private Dalamud dalamud;
/// <summary>
/// Initializes a new instance of the <see cref="Fate"/> class.
/// </summary>
@ -20,7 +22,7 @@ namespace Dalamud.Game.ClientState.Fates.Types
internal Fate(IntPtr address, Dalamud dalamud)
{
this.Address = address;
this.Dalamud = dalamud;
this.dalamud = dalamud;
}
/// <summary>
@ -28,10 +30,7 @@ namespace Dalamud.Game.ClientState.Fates.Types
/// </summary>
public IntPtr Address { get; }
/// <summary>
/// Gets Dalamud itself.
/// </summary>
private protected Dalamud Dalamud { get; }
private FFXIVClientStructs.FFXIV.Client.Game.Fate.FateContext* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Fate.FateContext*)this.Address;
public static bool operator ==(Fate fate1, Fate fate2)
{
@ -53,7 +52,7 @@ namespace Dalamud.Game.ClientState.Fates.Types
if (fate == null)
return false;
if (fate.Dalamud.ClientState.LocalContentId == 0)
if (fate.dalamud.ClientState.LocalContentId == 0)
return false;
return true;
@ -83,22 +82,22 @@ namespace Dalamud.Game.ClientState.Fates.Types
/// <summary>
/// Gets the Fate ID of this <see cref="Fate" />.
/// </summary>
public ushort FateId => *(ushort*)(this.Address + FateOffsets.FateId);
public ushort FateId => this.Struct->FateId;
/// <summary>
/// Gets game data linked to this Fate.
/// </summary>
public Lumina.Excel.GeneratedSheets.Fate GameData => this.Dalamud.Data.GetExcelSheet<Lumina.Excel.GeneratedSheets.Fate>().GetRow(this.FateId);
public Lumina.Excel.GeneratedSheets.Fate GameData => this.dalamud.Data.GetExcelSheet<Lumina.Excel.GeneratedSheets.Fate>().GetRow(this.FateId);
/// <summary>
/// Gets the time this <see cref="Fate"/> started.
/// </summary>
public int StartTimeEpoch => *(int*)(this.Address + FateOffsets.StartTimeEpoch);
public int StartTimeEpoch => this.Struct->StartTimeEpoch;
/// <summary>
/// Gets how long this <see cref="Fate"/> will run.
/// </summary>
public short Duration => *(short*)(this.Address + FateOffsets.Duration);
public short Duration => this.Struct->Duration;
/// <summary>
/// Gets the remaining time in seconds for this <see cref="Fate"/>.
@ -108,31 +107,31 @@ namespace Dalamud.Game.ClientState.Fates.Types
/// <summary>
/// Gets the displayname of this <see cref="Fate" />.
/// </summary>
public SeString Name => MemoryHelper.ReadSeString((Utf8String*)(this.Address + FateOffsets.Name));
public SeString Name => MemoryHelper.ReadSeString(&this.Struct->Name);
/// <summary>
/// Gets the state of this <see cref="Fate"/> (Running, Ended, Failed, Preparation, WaitingForEnd).
/// </summary>
public FateState State => *(FateState*)(this.Address + FateOffsets.State);
public FateState State => (FateState)this.Struct->State;
/// <summary>
/// Gets the progress amount of this <see cref="Fate"/>.
/// </summary>
public byte Progress => *(byte*)(this.Address + FateOffsets.Progress);
public byte Progress => this.Struct->Progress;
/// <summary>
/// Gets the level of this <see cref="Fate"/>.
/// </summary>
public byte Level => *(byte*)(this.Address + FateOffsets.Level);
public byte Level => this.Struct->Level;
/// <summary>
/// Gets the position of this <see cref="Fate"/>.
/// </summary>
public Position3 Position => *(Position3*)(this.Address + FateOffsets.Position);
public Vector3 Position => new(this.Struct->X, this.Struct->Y, this.Struct->Z);
/// <summary>
/// Gets the territory this <see cref="Fate"/> is located in.
/// </summary>
public TerritoryTypeResolver TerritoryType => new(*(ushort*)(this.Address + FateOffsets.Territory), this.Dalamud);
public ExcelResolver<Lumina.Excel.GeneratedSheets.TerritoryType> TerritoryType => new(this.Struct->TerritoryID, this.dalamud);
}
}

View file

@ -1,4 +1,4 @@
namespace Dalamud.Game.ClientState.Fates.Types
namespace Dalamud.Game.ClientState.Fates
{
/// <summary>
/// This represents the state of a single Fate.

View file

@ -2,7 +2,6 @@ using System;
using System.Collections;
using System.Collections.Generic;
using Dalamud.Game.ClientState.Fates.Types;
using JetBrains.Annotations;
using Serilog;
@ -13,11 +12,6 @@ namespace Dalamud.Game.ClientState.Fates
/// </summary>
public sealed partial class FateTable
{
// If the pointer at this offset is 0, do not scan the table
private const int CheckPtrOffset = 0x80;
private const int FirstPtrOffset = 0x90;
private const int LastPtrOffset = 0x98;
private readonly Dalamud dalamud;
private readonly ClientStateAddressResolver address;
@ -45,12 +39,13 @@ namespace Dalamud.Game.ClientState.Fates
if (fateTable == IntPtr.Zero)
return 0;
var check = *(long*)(fateTable + CheckPtrOffset);
// Sonar used this to check if the table was safe to read
var check = Struct->Unk80.ToInt64();
if (check == 0)
return 0;
var start = *(long*)(fateTable + FirstPtrOffset);
var end = *(long*)(fateTable + LastPtrOffset);
var start = Struct->FirstFatePtr.ToInt64();
var end = Struct->LastFatePtr.ToInt64();
if (start == 0 || end == 0)
return 0;
@ -58,7 +53,10 @@ namespace Dalamud.Game.ClientState.Fates
}
}
private unsafe IntPtr FateTableAddress
/// <summary>
/// Gets the address of the Fate table.
/// </summary>
internal unsafe IntPtr FateTableAddress
{
get
{
@ -69,6 +67,8 @@ namespace Dalamud.Game.ClientState.Fates
}
}
private unsafe FFXIVClientStructs.FFXIV.Client.Game.Fate.FateManager* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Fate.FateManager*)this.FateTableAddress;
/// <summary>
/// Get an actor at the specified spawn index.
/// </summary>
@ -80,22 +80,6 @@ namespace Dalamud.Game.ClientState.Fates
get
{
var address = this.GetFateAddress(index);
return this[address];
}
}
/// <summary>
/// Get a Fate at the specified address.
/// </summary>
/// <param name="address">The Fate address.</param>
/// <returns>A <see cref="Fate"/> at the specified address.</returns>
public Fate this[IntPtr address]
{
get
{
if (address == IntPtr.Zero)
return null;
return this.CreateFateReference(address);
}
}
@ -114,7 +98,7 @@ namespace Dalamud.Game.ClientState.Fates
if (fateTable == IntPtr.Zero)
return IntPtr.Zero;
var firstFate = *(IntPtr*)(fateTable + FirstPtrOffset);
var firstFate = this.Struct->FirstFatePtr;
return *(IntPtr*)(firstFate + (8 * index));
}
@ -139,20 +123,11 @@ namespace Dalamud.Game.ClientState.Fates
/// <summary>
/// This collection represents the currently available Fate events.
/// </summary>
public sealed partial class FateTable : IReadOnlyCollection<Fate>, ICollection
public sealed partial class FateTable : IReadOnlyCollection<Fate>
{
/// <inheritdoc/>
int IReadOnlyCollection<Fate>.Count => this.Length;
/// <inheritdoc/>
int ICollection.Count => this.Length;
/// <inheritdoc/>
bool ICollection.IsSynchronized => false;
/// <inheritdoc/>
object ICollection.SyncRoot => this;
/// <inheritdoc/>
public IEnumerator<Fate> GetEnumerator()
{
@ -164,15 +139,5 @@ namespace Dalamud.Game.ClientState.Fates
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
/// <inheritdoc/>
void ICollection.CopyTo(Array array, int index)
{
for (var i = 0; i < this.Length; i++)
{
array.SetValue(this[i], index);
index++;
}
}
}
}

View file

@ -1,21 +0,0 @@
using System.Diagnostics.CodeAnalysis;
namespace Dalamud.Game.ClientState.Fates.Types
{
/// <summary>
/// Memory offsets for the <see cref="Fate"/> type.
/// </summary>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Document the offset usage instead.")]
public static class FateOffsets
{
public const int FateId = 0x18;
public const int StartTimeEpoch = 0x20;
public const int Duration = 0x28;
public const int Name = 0xC0;
public const int State = 0x3AC;
public const int Progress = 0x3B8;
public const int Level = 0x3F9;
public const int Position = 0x450;
public const int Territory = 0x74E;
}
}

View file

@ -1,6 +1,6 @@
using System;
using System;
namespace Dalamud.Game.ClientState
namespace Dalamud.Game.ClientState.GamePad
{
/// <summary>
/// Bitmask of the Button ushort used by the game.

View file

@ -1,6 +1,6 @@
using System.Runtime.InteropServices;
namespace Dalamud.Game.ClientState.Structs
namespace Dalamud.Game.ClientState.GamePad
{
/// <summary>
/// Struct which gets populated by polling the gamepads.

View file

@ -1,11 +1,10 @@
using System;
using Dalamud.Game.ClientState.Structs;
using Dalamud.Hooking;
using ImGuiNET;
using Serilog;
namespace Dalamud.Game.ClientState
namespace Dalamud.Game.ClientState.GamePad
{
/// <summary>
/// Exposes the game gamepad state to dalamud.
@ -24,7 +23,7 @@ namespace Dalamud.Game.ClientState
private int rightStickY;
/// <summary>
/// Initializes a new instance of the <see cref="GamepadState" /> class.
/// Initializes a new instance of the <see cref="GamepadState" /> class.
/// </summary>
/// <param name="resolver">Resolver knowing the pointer to the GamepadPoll function.</param>
public GamepadState(ClientStateAddressResolver resolver)
@ -43,12 +42,10 @@ namespace Dalamud.Game.ClientState
private delegate int ControllerPoll(IntPtr controllerInput);
#if DEBUG
/// <summary>
/// Gets the pointer to the current instance of the GamepadInput struct.
/// </summary>
public IntPtr GamepadInput { get; private set; }
#endif
public IntPtr GamepadInputAddress { get; private set; }
/// <summary>
/// Gets the state of the left analogue stick in the left direction between 0 (not tilted) and 1 (max tilt).
@ -189,9 +186,7 @@ namespace Dalamud.Game.ClientState
var original = this.gamepadPoll.Original(gamepadInput);
try
{
#if DEBUG
this.GamepadInput = gamepadInput;
#endif
this.GamepadInputAddress = gamepadInput;
var input = (GamepadInput*)gamepadInput;
this.leftStickX = input->LeftStickX;
this.leftStickY = input->LeftStickY;

View file

@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
using Serilog;
namespace Dalamud.Game.ClientState
namespace Dalamud.Game.ClientState.Keys
{
/// <summary>
/// Wrapper around the game keystate buffer, which contains the pressed state for all keyboard keys, indexed by virtual vkCode.
@ -52,6 +52,13 @@ namespace Dalamud.Game.ClientState
}
}
/// <summary>
/// Get or set the keypressed state for a given VirtualKey enum.
/// </summary>
/// <param name="vk">The virtual key to change.</param>
/// <returns>Whether the specified key is currently pressed.</returns>
public bool this[VirtualKey vk] => this[(int)vk];
/// <summary>
/// Clears the pressed state for all keys.
/// </summary>

File diff suppressed because it is too large Load diff

View file

@ -1,19 +0,0 @@
namespace Dalamud.Game.ClientState.Resolvers
{
/// <summary>
/// This object represents a class or job.
/// </summary>
public class ClassJobResolver : BaseResolver<Lumina.Excel.GeneratedSheets.ClassJob>
{
/// <summary>
/// Initializes a new instance of the <see cref="ClassJobResolver"/> class.
/// Set up the ClassJob resolver with the provided ID.
/// </summary>
/// <param name="id">The ID of the classJob.</param>
/// <param name="dalamud">The Dalamud instance.</param>
internal ClassJobResolver(ushort id, Dalamud dalamud)
: base(id, dalamud)
{
}
}
}

View file

@ -3,19 +3,19 @@ using Lumina.Excel;
namespace Dalamud.Game.ClientState.Resolvers
{
/// <summary>
/// This object represents a class or job.
/// This object resolves a rowID within an Excel sheet.
/// </summary>
/// <typeparam name="T">The type of Lumina sheet to resolve.</typeparam>
public class BaseResolver<T> where T : ExcelRow
public class ExcelResolver<T> where T : ExcelRow
{
private readonly Dalamud dalamud;
/// <summary>
/// Initializes a new instance of the <see cref="BaseResolver{T}"/> class.
/// Initializes a new instance of the <see cref="ExcelResolver{T}"/> class.
/// </summary>
/// <param name="id">The ID of the classJob.</param>
/// <param name="dalamud">The Dalamud instance.</param>
internal BaseResolver(uint id, Dalamud dalamud)
internal ExcelResolver(uint id, Dalamud dalamud)
{
this.dalamud = dalamud;
this.Id = id;

View file

@ -1,19 +0,0 @@
namespace Dalamud.Game.ClientState.Resolvers
{
/// <summary>
/// This object represents a Fate a character can participate in.
/// </summary>
public class FateResolver : BaseResolver<Lumina.Excel.GeneratedSheets.Fate>
{
/// <summary>
/// Initializes a new instance of the <see cref="FateResolver"/> class.
/// Set up the Fate resolver with the provided ID.
/// </summary>
/// <param name="id">The ID of the Fate.</param>
/// <param name="dalamud">The Dalamud instance.</param>
internal FateResolver(ushort id, Dalamud dalamud)
: base(id, dalamud)
{
}
}
}

View file

@ -1,19 +0,0 @@
namespace Dalamud.Game.ClientState.Resolvers
{
/// <summary>
/// This object represents a territory a character can be in.
/// </summary>
public class TerritoryTypeResolver : BaseResolver<Lumina.Excel.GeneratedSheets.TerritoryType>
{
/// <summary>
/// Initializes a new instance of the <see cref="TerritoryTypeResolver"/> class.
/// Set up the territory type resolver with the provided ID.
/// </summary>
/// <param name="id">The ID of the territory type.</param>
/// <param name="dalamud">The Dalamud instance.</param>
internal TerritoryTypeResolver(ushort id, Dalamud dalamud)
: base(id, dalamud)
{
}
}
}

View file

@ -1,19 +0,0 @@
namespace Dalamud.Game.ClientState.Resolvers
{
/// <summary>
/// This object represents a world a character can reside on.
/// </summary>
public class WorldResolver : BaseResolver<Lumina.Excel.GeneratedSheets.World>
{
/// <summary>
/// Initializes a new instance of the <see cref="WorldResolver"/> class.
/// Set up the world resolver with the provided ID.
/// </summary>
/// <param name="id">The ID of the world.</param>
/// <param name="dalamud">The Dalamud instance.</param>
internal WorldResolver(ushort id, Dalamud dalamud)
: base(id, dalamud)
{
}
}
}

View file

@ -5,13 +5,13 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Dalamud.Game.Internal.Gui;
using Dalamud.Game.Internal.Libc;
using Dalamud.Game.Internal.Network;
using Dalamud.Game.Gui;
using Dalamud.Game.Libc;
using Dalamud.Game.Network;
using Dalamud.Hooking;
using Serilog;
namespace Dalamud.Game.Internal
namespace Dalamud.Game
{
/// <summary>
/// This class represents the Framework of the native game client and grants access to various subsystems.
@ -90,7 +90,7 @@ namespace Dalamud.Game.Internal
/// <summary>
/// Gets the stats history mapping.
/// </summary>
public static Dictionary<string, List<double>> StatsHistory = new();
public static Dictionary<string, List<double>> StatsHistory { get; } = new();
#region Subsystems

View file

@ -1,7 +1,9 @@
using System;
using System.Runtime.InteropServices;
namespace Dalamud.Game.Internal
using Dalamud.Game.Internal;
namespace Dalamud.Game
{
/// <summary>
/// The address resolver for the <see cref="Framework"/> class.

View file

@ -209,6 +209,32 @@ namespace Dalamud.Game
return v2 <= v1;
}
public static GameVersion operator +(GameVersion v1, TimeSpan v2)
{
if (v1 == null)
throw new ArgumentNullException(nameof(v1));
if (v1.Year == -1 || v1.Month == -1 || v1.Day == -1)
return v1;
var date = new DateTime(v1.Year, v1.Month, v1.Day) + v2;
return new GameVersion(date.Year, date.Month, date.Day, v1.Major, v1.Minor);
}
public static GameVersion operator -(GameVersion v1, TimeSpan v2)
{
if (v1 == null)
throw new ArgumentNullException(nameof(v1));
if (v1.Year == -1 || v1.Month == -1 || v1.Day == -1)
return v1;
var date = new DateTime(v1.Year, v1.Month, v1.Day) - v2;
return new GameVersion(date.Year, date.Month, date.Day, v1.Major, v1.Minor);
}
/// <summary>
/// Parse a version string. YYYY.MM.DD.majr.minr or "any".
/// </summary>

View file

@ -1,66 +1,63 @@
using System;
namespace Dalamud.Game.Internal.Gui.Addon
using Dalamud.Memory;
namespace Dalamud.Game.Gui.Addons
{
/// <summary>
/// This class represents an in-game UI "Addon".
/// </summary>
public class Addon
public unsafe class Addon
{
/// <summary>
/// The address of the addon.
/// </summary>
public IntPtr Address;
/// <summary>
/// The addon interop data.
/// </summary>
protected Structs.Addon addonStruct;
/// <summary>
/// Initializes a new instance of the <see cref="Addon"/> class.
/// </summary>
/// <param name="address">The address of the addon.</param>
/// <param name="addonStruct">The addon interop data.</param>
public Addon(IntPtr address, Structs.Addon addonStruct)
public Addon(IntPtr address)
{
this.Address = address;
this.addonStruct = addonStruct;
}
/// <summary>
/// Gets the address of the addon.
/// </summary>
public IntPtr Address { get; }
/// <summary>
/// Gets the name of the addon.
/// </summary>
public string Name => this.addonStruct.Name;
public string Name => MemoryHelper.ReadString((IntPtr)this.Struct->Name, 0x20);
/// <summary>
/// Gets the X position of the addon on screen.
/// </summary>
public short X => this.addonStruct.X;
public short X => this.Struct->X;
/// <summary>
/// Gets the Y position of the addon on screen.
/// </summary>
public short Y => this.addonStruct.Y;
public short Y => this.Struct->Y;
/// <summary>
/// Gets the scale of the addon.
/// </summary>
public float Scale => this.addonStruct.Scale;
public float Scale => this.Struct->Scale;
/// <summary>
/// Gets the width of the addon. This may include non-visible parts.
/// </summary>
public unsafe float Width => this.addonStruct.RootNode->Width * this.Scale;
public unsafe float Width => this.Struct->RootNode->Width * this.Scale;
/// <summary>
/// Gets the height of the addon. This may include non-visible parts.
/// </summary>
public unsafe float Height => this.addonStruct.RootNode->Height * this.Scale;
public unsafe float Height => this.Struct->RootNode->Height * this.Scale;
/// <summary>
/// Gets a value indicating whether the addon is visible.
/// </summary>
public bool Visible => (this.addonStruct.Flags & 0x20) == 0x20;
public bool Visible => this.Struct->IsVisible;
private FFXIVClientStructs.FFXIV.Component.GUI.AtkUnitBase* Struct => (FFXIVClientStructs.FFXIV.Component.GUI.AtkUnitBase*)this.Address;
}
}

View file

@ -2,16 +2,15 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Dalamud.Game.Internal.Libc;
using Dalamud.Game.Libc;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Hooking;
using Serilog;
namespace Dalamud.Game.Internal.Gui
namespace Dalamud.Game.Gui
{
/// <summary>
/// This class handles interacting with the native chat UI.
@ -60,17 +59,6 @@ namespace Dalamud.Game.Internal.Gui
/// <param name="isHandled">A value indicating whether the message was handled or should be propagated.</param>
public delegate void OnMessageDelegate(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled);
/// <summary>
/// A delegate type used with the <see cref="OnChatMessageRaw"/> event.
/// </summary>
/// <param name="type">The type of chat.</param>
/// <param name="senderId">The sender ID.</param>
/// <param name="sender">The sender name.</param>
/// <param name="message">The message sent.</param>
/// <param name="isHandled">A value indicating whether the message was handled or should be propagated.</param>
[Obsolete("Please use OnMessageDelegate instead. For modifications, it will take precedence.")]
public delegate void OnMessageRawDelegate(XivChatType type, uint senderId, ref StdString sender, ref StdString message, ref bool isHandled);
/// <summary>
/// A delegate type used with the <see cref="OnCheckMessageHandled"/> event.
/// </summary>
@ -113,12 +101,6 @@ namespace Dalamud.Game.Internal.Gui
/// </summary>
public event OnMessageDelegate OnChatMessage;
/// <summary>
/// Event that will be fired when a chat message is sent by the game, containing raw, unparsed data.
/// </summary>
[Obsolete("Please use OnChatMessage instead. For modifications, it will take precedence.")]
public event OnMessageRawDelegate OnChatMessageRaw;
/// <summary>
/// Event that allows you to stop messages from appearing in chat by setting the isHandled parameter to true.
/// </summary>
@ -353,7 +335,7 @@ namespace Dalamud.Game.Internal.Gui
this.LastLinkedItemId = Marshal.ReadInt32(itemInfoPtr, 8);
this.LastLinkedItemFlags = Marshal.ReadByte(itemInfoPtr, 0x14);
Log.Debug($"HandlePopulateItemLinkDetour {linkObjectPtr} {itemInfoPtr} - linked:{this.LastLinkedItemId}");
Log.Verbose($"HandlePopulateItemLinkDetour {linkObjectPtr} {itemInfoPtr} - linked:{this.LastLinkedItemId}");
}
catch (Exception ex)
{
@ -388,7 +370,6 @@ namespace Dalamud.Game.Internal.Gui
if (!isHandled)
{
this.OnChatMessage?.Invoke(chattype, senderid, ref parsedSender, ref parsedMessage, ref isHandled);
this.OnChatMessageRaw?.Invoke(chattype, senderid, ref sender, ref message, ref isHandled);
}
var newEdited = parsedMessage.Encode();

View file

@ -1,6 +1,8 @@
using System;
namespace Dalamud.Game.Internal.Gui
using Dalamud.Game.Internal;
namespace Dalamud.Game.Gui
{
/// <summary>
/// The address resolver for the <see cref="ChatGui"/> class.
@ -102,7 +104,7 @@ namespace Dalamud.Game.Internal.Gui
protected override void Setup64Bit(SigScanner sig)
{
// PrintMessage = sig.ScanText("4055 57 41 ?? 41 ?? 488DAC24D8FEFFFF 4881EC28020000 488B05???????? 4833C4 488985F0000000 4532D2 48894C2448"); LAST PART FOR 5.1???
this.PrintMessage = sig.ScanText("4055 53 56 4154 4157 48 8d ac 24 ?? ?? ?? ?? 48 81 ec 20 02 00 00 48 8b 05");
this.PrintMessage = sig.ScanText("40 55 53 56 41 54 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC 20 02 00 00 48 8B 05");
// PrintMessage = sig.ScanText("4055 57 41 ?? 41 ?? 488DAC24E8FEFFFF 4881EC18020000 488B05???????? 4833C4 488985E0000000 4532D2 48894C2438"); old
// PrintMessage = sig.ScanText("40 55 57 41 56 41 57 48 8D AC 24 D8 FE FF FF 48 81 EC 28 02 00 00 48 8B 05 63 47 4A 01 48 33 C4 48 89 85 F0 00 00 00 45 32 D2 48 89 4C 24 48 33");

View file

@ -1,25 +1,23 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using Dalamud.Game.Gui.Addons;
using Dalamud.Game.Gui.PartyFinder;
using Dalamud.Game.Internal.Gui;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Hooking;
using Dalamud.Interface;
using Dalamud.Utility;
using Serilog;
using SharpDX;
namespace Dalamud.Game.Internal.Gui
namespace Dalamud.Game.Gui
{
/// <summary>
/// A class handling many aspects of the in-game UI.
/// </summary>
public sealed class GameGui : IDisposable
{
/// <summary>
/// The delegate of the native method that gets the Client::UI::UIModule address.
/// </summary>
/// <returns>The Client::UI::UIModule address.</returns>
public readonly GetBaseUIObjectDelegate GetBaseUIObject;
private readonly Dalamud dalamud;
private readonly GameGuiAddressResolver address;
@ -147,6 +145,12 @@ namespace Dalamud.Game.Internal.Gui
/// </summary>
public event EventHandler<bool> OnUiHideToggled;
/// <summary>
/// Gets a callable delegate for the GetBaseUIObject game method.
/// </summary>
/// <returns>The Client::UI::UIModule address.</returns>
public GetBaseUIObjectDelegate GetBaseUIObject { get; }
/// <summary>
/// Gets the <see cref="Chat"/> instance.
/// </summary>
@ -233,13 +237,13 @@ namespace Dalamud.Game.Internal.Gui
/// <param name="worldPos">Coordinates in the world.</param>
/// <param name="screenPos">Converted coordinates.</param>
/// <returns>True if worldPos corresponds to a position in front of the camera.</returns>
public bool WorldToScreen(Vector3 worldPos, out Vector2 screenPos)
public bool WorldToScreen(SharpDX.Vector3 worldPos, out SharpDX.Vector2 screenPos)
{
// Get base object with matrices
var matrixSingleton = this.getMatrixSingleton();
// Read current ViewProjectionMatrix plus game window size
var viewProjectionMatrix = default(Matrix);
var viewProjectionMatrix = default(SharpDX.Matrix);
float width, height;
var windowPos = ImGuiHelpers.MainViewport.Pos;
@ -254,9 +258,9 @@ namespace Dalamud.Game.Internal.Gui
height = *(rawMatrix + 1);
}
Vector3.Transform(ref worldPos, ref viewProjectionMatrix, out Vector3 pCoords);
SharpDX.Vector3.Transform(ref worldPos, ref viewProjectionMatrix, out SharpDX.Vector3 pCoords);
screenPos = new Vector2(pCoords.X / pCoords.Z, pCoords.Y / pCoords.Z);
screenPos = new SharpDX.Vector2(pCoords.X / pCoords.Z, pCoords.Y / pCoords.Z);
screenPos.X = (0.5f * width * (screenPos.X + 1f)) + windowPos.X;
screenPos.Y = (0.5f * height * (1f - screenPos.Y)) + windowPos.Y;
@ -266,6 +270,39 @@ namespace Dalamud.Game.Internal.Gui
screenPos.Y > windowPos.Y && screenPos.Y < windowPos.Y + height;
}
/// <summary>
/// Converts in-world coordinates to screen coordinates (upper left corner origin).
/// </summary>
/// <param name="worldPos">Coordinates in the world.</param>
/// <param name="screenPos">Converted coordinates.</param>
/// <returns>True if worldPos corresponds to a position in front of the camera.</returns>
/// <remarks>
/// This overload requires a conversion to SharpDX vectors, however the penalty should be negligible.
/// </remarks>
public bool WorldToScreen(Vector3 worldPos, out Vector2 screenPos)
{
var result = this.WorldToScreen(worldPos.ToSharpDX(), out var sharpScreenPos);
screenPos = sharpScreenPos.ToSystem();
return result;
}
/// <summary>
/// Converts in-world coordinates to screen coordinates (upper left corner origin).
/// </summary>
/// <param name="worldPos">Coordinates in the world.</param>
/// <param name="screenPos">Converted coordinates.</param>
/// <returns>True if worldPos corresponds to a position in front of the camera.</returns>
/// <remarks>
/// This overload requires a conversion to SharpDX vectors, however the penalty should be negligible.
/// </remarks>
public bool WorldToScreen(Position3 worldPos, out Vector2 screenPos)
{
// This overload is necessary due to Positon3 implicit operators.
var result = this.WorldToScreen((SharpDX.Vector3)worldPos, out var sharpScreenPos);
screenPos = sharpScreenPos.ToSystem();
return result;
}
/// <summary>
/// Converts screen coordinates to in-world coordinates via raycasting.
/// </summary>
@ -273,7 +310,7 @@ namespace Dalamud.Game.Internal.Gui
/// <param name="worldPos">Converted coordinates.</param>
/// <param name="rayDistance">How far to search for a collision.</param>
/// <returns>True if successful. On false, worldPos's contents are undefined.</returns>
public bool ScreenToWorld(Vector2 screenPos, out Vector3 worldPos, float rayDistance = 100000.0f)
public bool ScreenToWorld(SharpDX.Vector2 screenPos, out SharpDX.Vector3 worldPos, float rayDistance = 100000.0f)
{
// The game is only visible in the main viewport, so if the cursor is outside
// of the game window, do not bother calculating anything
@ -291,7 +328,7 @@ namespace Dalamud.Game.Internal.Gui
var matrixSingleton = this.getMatrixSingleton();
// Read current ViewProjectionMatrix plus game window size
var viewProjectionMatrix = default(Matrix);
var viewProjectionMatrix = default(SharpDX.Matrix);
float width, height;
unsafe
{
@ -306,18 +343,18 @@ namespace Dalamud.Game.Internal.Gui
viewProjectionMatrix.Invert();
var localScreenPos = new Vector2(screenPos.X - windowPos.X, screenPos.Y - windowPos.Y);
var screenPos3D = new Vector3
var localScreenPos = new SharpDX.Vector2(screenPos.X - windowPos.X, screenPos.Y - windowPos.Y);
var screenPos3D = new SharpDX.Vector3
{
X = (localScreenPos.X / width * 2.0f) - 1.0f,
Y = -((localScreenPos.Y / height * 2.0f) - 1.0f),
Z = 0,
};
Vector3.TransformCoordinate(ref screenPos3D, ref viewProjectionMatrix, out var camPos);
SharpDX.Vector3.TransformCoordinate(ref screenPos3D, ref viewProjectionMatrix, out var camPos);
screenPos3D.Z = 1;
Vector3.TransformCoordinate(ref screenPos3D, ref viewProjectionMatrix, out var camPosOne);
SharpDX.Vector3.TransformCoordinate(ref screenPos3D, ref viewProjectionMatrix, out var camPosOne);
var clipPos = camPosOne - camPos;
clipPos.Normalize();
@ -347,7 +384,7 @@ namespace Dalamud.Game.Internal.Gui
}
}
worldPos = new Vector3
worldPos = new SharpDX.Vector3
{
X = worldPosArray[0],
Y = worldPosArray[1],
@ -358,6 +395,23 @@ namespace Dalamud.Game.Internal.Gui
return isSuccess;
}
/// <summary>
/// Converts screen coordinates to in-world coordinates via raycasting.
/// </summary>
/// <param name="screenPos">Screen coordinates.</param>
/// <param name="worldPos">Converted coordinates.</param>
/// <param name="rayDistance">How far to search for a collision.</param>
/// <returns>True if successful. On false, worldPos's contents are undefined.</returns>
/// <remarks>
/// This overload requires a conversion to SharpDX vectors, however the penalty should be negligible.
/// </remarks>
public bool ScreenToWorld(Vector2 screenPos, out Vector3 worldPos, float rayDistance = 100000.0f)
{
var result = this.ScreenToWorld(screenPos.ToSharpDX(), out var sharpworldPos);
worldPos = sharpworldPos.ToSystem();
return result;
}
/// <summary>
/// Gets a pointer to the game's UI module.
/// </summary>
@ -385,12 +439,14 @@ namespace Dalamud.Game.Internal.Gui
/// <param name="name">The addon name.</param>
/// <param name="index">The index of the addon, starting at 1.</param>
/// <returns>The native memory representation of the addon, if it exists.</returns>
public Addon.Addon GetAddonByName(string name, int index)
public Addon GetAddonByName(string name, int index)
{
var addonMem = this.GetUiObjectByName(name, index);
if (addonMem == IntPtr.Zero) return null;
var addonStruct = Marshal.PtrToStructure<Structs.Addon>(addonMem);
return new Addon.Addon(addonMem, addonStruct);
var address = this.GetUiObjectByName(name, index);
if (address == IntPtr.Zero)
return null;
return new Addon(address);
}
/// <summary>

View file

@ -1,7 +1,7 @@
using System;
using System.Runtime.InteropServices;
namespace Dalamud.Game.Internal.Gui
namespace Dalamud.Game.Gui
{
/// <summary>
/// The address resolver for the <see cref="GameGui"/> class.

View file

@ -1,4 +1,4 @@
namespace Dalamud.Game.Internal.Gui
namespace Dalamud.Game.Gui
{
/// <summary>
/// ActionKinds used in AgentActionDetail.

View file

@ -1,4 +1,4 @@
namespace Dalamud.Game.Internal.Gui
namespace Dalamud.Game.Gui
{
/// <summary>
/// This class represents the hotbar action currently hovered over by the cursor.

View file

@ -0,0 +1,28 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace Dalamud.Game.Gui.PartyFinder.Internal
{
/// <summary>
/// The structure of the PartyFinder packet.
/// </summary>
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "Sequential struct marshaling.")]
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Sequential struct marshaling.")]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Document the field usage.")]
[StructLayout(LayoutKind.Sequential)]
internal readonly struct PartyFinderPacket
{
/// <summary>
/// Gets the size of this packet.
/// </summary>
internal static int PacketSize { get; } = Marshal.SizeOf<PartyFinderPacket>();
internal readonly int BatchNumber;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
private readonly byte[] padding1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
internal readonly PartyFinderPacketListing[] Listings;
}
}

View file

@ -0,0 +1,99 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
namespace Dalamud.Game.Gui.PartyFinder.Internal
{
/// <summary>
/// The structure of an individual listing within a packet.
/// </summary>
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Sequential struct marshaling.")]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Document the field usage.")]
[StructLayout(LayoutKind.Sequential)]
internal readonly struct PartyFinderPacketListing
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
private readonly byte[] header1;
internal readonly uint Id;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
private readonly byte[] header2;
internal readonly uint ContentIdLower;
private readonly ushort unknownShort1;
private readonly ushort unknownShort2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
private readonly byte[] header3;
internal readonly byte Category;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
private readonly byte[] header4;
internal readonly ushort Duty;
internal readonly byte DutyType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
private readonly byte[] header5;
internal readonly ushort World;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
private readonly byte[] header6;
internal readonly byte Objective;
internal readonly byte BeginnersWelcome;
internal readonly byte Conditions;
internal readonly byte DutyFinderSettings;
internal readonly byte LootRules;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
private readonly byte[] header7; // all zero in every pf I've examined
private readonly uint lastPatchHotfixTimestamp; // last time the servers were restarted?
internal readonly ushort SecondsRemaining;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
private readonly byte[] header8; // 00 00 01 00 00 00 in every pf I've examined
internal readonly ushort MinimumItemLevel;
internal readonly ushort HomeWorld;
internal readonly ushort CurrentWorld;
private readonly byte header9;
internal readonly byte NumSlots;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
private readonly byte[] header10;
internal readonly byte SearchArea;
private readonly byte header11;
internal readonly byte NumParties;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
private readonly byte[] header12; // 00 00 00 always. maybe numParties is a u32?
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
internal readonly uint[] Slots;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
internal readonly byte[] JobsPresent;
// Note that ByValTStr will not work here because the strings are UTF-8 and there's only a CharSet for UTF-16 in C#.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
internal readonly byte[] Name;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 192)]
internal readonly byte[] Description;
internal bool IsNull()
{
// a valid party finder must have at least one slot set
return this.Slots.All(slot => slot == 0);
}
}
}

View file

@ -1,11 +1,11 @@
using System;
namespace Dalamud.Game.Internal.Gui
namespace Dalamud.Game.Gui.PartyFinder
{
/// <summary>
/// The address resolver for the <see cref="PartyFinderGui"/> class.
/// </summary>
internal class PartyFinderAddressResolver : BaseAddressResolver
public class PartyFinderAddressResolver : BaseAddressResolver
{
/// <summary>
/// Gets the address of the native ReceiveListing method.

View file

@ -1,11 +1,12 @@
using System;
using System.Runtime.InteropServices;
using Dalamud.Game.Internal.Gui.Structs;
using Dalamud.Game.Gui.PartyFinder.Internal;
using Dalamud.Game.Gui.PartyFinder.Types;
using Dalamud.Hooking;
using Serilog;
namespace Dalamud.Game.Internal.Gui
namespace Dalamud.Game.Gui.PartyFinder
{
/// <summary>
/// This class handles interacting with the native PartyFinder window.
@ -30,7 +31,7 @@ namespace Dalamud.Game.Internal.Gui
this.address = new PartyFinderAddressResolver();
this.address.Setup(scanner);
this.memory = Marshal.AllocHGlobal(PartyFinder.PacketInfo.PacketSize);
this.memory = Marshal.AllocHGlobal(PartyFinderPacket.PacketSize);
this.receiveListingHook = new Hook<ReceiveListingDelegate>(this.address.ReceiveListing, new ReceiveListingDelegate(this.HandleReceiveListingDetour));
}
@ -87,7 +88,7 @@ namespace Dalamud.Game.Internal.Gui
{
var dataPtr = data + 0x10;
var packet = Marshal.PtrToStructure<PartyFinder.Packet>(dataPtr);
var packet = Marshal.PtrToStructure<PartyFinderPacket>(dataPtr);
// rewriting is an expensive operation, so only do it if necessary
var needToRewrite = false;
@ -125,33 +126,8 @@ namespace Dalamud.Game.Internal.Gui
// copy our new memory over the game's
unsafe
{
Buffer.MemoryCopy((void*)this.memory, (void*)dataPtr, PartyFinder.PacketInfo.PacketSize, PartyFinder.PacketInfo.PacketSize);
Buffer.MemoryCopy((void*)this.memory, (void*)dataPtr, PartyFinderPacket.PacketSize, PartyFinderPacket.PacketSize);
}
}
}
/// <summary>
/// This class represents additional arguments passed by the game.
/// </summary>
public class PartyFinderListingEventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="PartyFinderListingEventArgs"/> class.
/// </summary>
/// <param name="batchNumber">The batch number.</param>
internal PartyFinderListingEventArgs(int batchNumber)
{
this.BatchNumber = batchNumber;
}
/// <summary>
/// Gets the batch number.
/// </summary>
public int BatchNumber { get; }
/// <summary>
/// Gets or sets a value indicating whether the listing is visible.
/// </summary>
public bool Visible { get; set; } = true;
}
}

View file

@ -0,0 +1,48 @@
namespace Dalamud.Game.Gui.PartyFinder.Types
{
/// <summary>
/// Category flags for the <see cref="PartyFinder"/> class.
/// </summary>
public enum DutyFinderCategory
{
/// <summary>
/// The duty category.
/// </summary>
Duty = 0,
/// <summary>
/// The quest battle category.
/// </summary>
QuestBattles = 1 << 0,
/// <summary>
/// The fate category.
/// </summary>
Fates = 1 << 1,
/// <summary>
/// The treasure hunt category.
/// </summary>
TreasureHunt = 1 << 2,
/// <summary>
/// The hunt category.
/// </summary>
TheHunt = 1 << 3,
/// <summary>
/// The gathering forays category.
/// </summary>
GatheringForays = 1 << 4,
/// <summary>
/// The deep dungeons category.
/// </summary>
DeepDungeons = 1 << 5,
/// <summary>
/// The adventuring forays category.
/// </summary>
AdventuringForays = 1 << 6,
}
}

View file

@ -0,0 +1,26 @@
using System;
namespace Dalamud.Game.Gui.PartyFinder.Types
{
/// <summary>
/// Condition flags for the <see cref="PartyFinder"/> class.
/// </summary>
[Flags]
public enum DutyFinderConditionFlags : uint
{
/// <summary>
/// No duty condition.
/// </summary>
None = 1,
/// <summary>
/// The duty complete condition.
/// </summary>
DutyComplete = 2,
/// <summary>
/// The duty incomplete condition.
/// </summary>
DutyIncomplete = 4,
}
}

View file

@ -0,0 +1,26 @@
using System;
namespace Dalamud.Game.Gui.PartyFinder.Types
{
/// <summary>
/// Loot rule flags for the <see cref="PartyFinder"/> class.
/// </summary>
[Flags]
public enum DutyFinderLootRuleFlags : uint
{
/// <summary>
/// No loot rules.
/// </summary>
None = 0,
/// <summary>
/// The greed only rule.
/// </summary>
GreedOnly = 1,
/// <summary>
/// The lootmaster rule.
/// </summary>
Lootmaster = 2,
}
}

View file

@ -0,0 +1,31 @@
using System;
namespace Dalamud.Game.Gui.PartyFinder.Types
{
/// <summary>
/// Objective flags for the <see cref="PartyFinder"/> class.
/// </summary>
[Flags]
public enum DutyFinderObjectiveFlags : uint
{
/// <summary>
/// No objective.
/// </summary>
None = 0,
/// <summary>
/// The duty completion objective.
/// </summary>
DutyCompletion = 1,
/// <summary>
/// The practice objective.
/// </summary>
Practice = 2,
/// <summary>
/// The loot objective.
/// </summary>
Loot = 4,
}
}

View file

@ -0,0 +1,36 @@
using System;
namespace Dalamud.Game.Gui.PartyFinder.Types
{
/// <summary>
/// Search area flags for the <see cref="PartyFinder"/> class.
/// </summary>
[Flags]
public enum DutyFinderSearchAreaFlags : uint
{
/// <summary>
/// Datacenter.
/// </summary>
DataCentre = 1 << 0,
/// <summary>
/// Private.
/// </summary>
Private = 1 << 1,
/// <summary>
/// Alliance raid.
/// </summary>
AllianceRaid = 1 << 2,
/// <summary>
/// World.
/// </summary>
World = 1 << 3,
/// <summary>
/// One player per job.
/// </summary>
OnePlayerPerJob = 1 << 5,
}
}

View file

@ -0,0 +1,31 @@
using System;
namespace Dalamud.Game.Gui.PartyFinder.Types
{
/// <summary>
/// Duty finder settings flags for the <see cref="PartyFinder"/> class.
/// </summary>
[Flags]
public enum DutyFinderSettingsFlags : uint
{
/// <summary>
/// No duty finder settings.
/// </summary>
None = 0,
/// <summary>
/// The undersized party setting.
/// </summary>
UndersizedParty = 1 << 0,
/// <summary>
/// The minimum item level setting.
/// </summary>
MinimumItemLevel = 1 << 1,
/// <summary>
/// The silence echo setting.
/// </summary>
SilenceEcho = 1 << 2,
}
}

View file

@ -0,0 +1,23 @@
namespace Dalamud.Game.Gui.PartyFinder.Types
{
/// <summary>
/// Duty type flags for the <see cref="PartyFinder"/> class.
/// </summary>
public enum DutyType
{
/// <summary>
/// No duty type.
/// </summary>
Other = 0,
/// <summary>
/// The roulette duty type.
/// </summary>
Roulette = 1 << 0,
/// <summary>
/// The normal duty type.
/// </summary>
Normal = 1 << 1,
}
}

View file

@ -0,0 +1,146 @@
using System;
namespace Dalamud.Game.Gui.PartyFinder.Types
{
/// <summary>
/// Job flags for the <see cref="PartyFinder"/> class.
/// </summary>
[Flags]
public enum JobFlags
{
/// <summary>
/// Gladiator (GLD).
/// </summary>
Gladiator = 1 << 1,
/// <summary>
/// Pugilist (PGL).
/// </summary>
Pugilist = 1 << 2,
/// <summary>
/// Marauder (MRD).
/// </summary>
Marauder = 1 << 3,
/// <summary>
/// Lancer (LNC).
/// </summary>
Lancer = 1 << 4,
/// <summary>
/// Archer (ARC).
/// </summary>
Archer = 1 << 5,
/// <summary>
/// Conjurer (CNJ).
/// </summary>
Conjurer = 1 << 6,
/// <summary>
/// Thaumaturge (THM).
/// </summary>
Thaumaturge = 1 << 7,
/// <summary>
/// Paladin (PLD).
/// </summary>
Paladin = 1 << 8,
/// <summary>
/// Monk (MNK).
/// </summary>
Monk = 1 << 9,
/// <summary>
/// Warrior (WAR).
/// </summary>
Warrior = 1 << 10,
/// <summary>
/// Dragoon (DRG).
/// </summary>
Dragoon = 1 << 11,
/// <summary>
/// Bard (BRD).
/// </summary>
Bard = 1 << 12,
/// <summary>
/// White mage (WHM).
/// </summary>
WhiteMage = 1 << 13,
/// <summary>
/// Black mage (BLM).
/// </summary>
BlackMage = 1 << 14,
/// <summary>
/// Arcanist (ACN).
/// </summary>
Arcanist = 1 << 15,
/// <summary>
/// Summoner (SMN).
/// </summary>
Summoner = 1 << 16,
/// <summary>
/// Scholar (SCH).
/// </summary>
Scholar = 1 << 17,
/// <summary>
/// Rogue (ROG).
/// </summary>
Rogue = 1 << 18,
/// <summary>
/// Ninja (NIN).
/// </summary>
Ninja = 1 << 19,
/// <summary>
/// Machinist (MCH).
/// </summary>
Machinist = 1 << 20,
/// <summary>
/// Dark Knight (DRK).
/// </summary>
DarkKnight = 1 << 21,
/// <summary>
/// Astrologian (AST).
/// </summary>
Astrologian = 1 << 22,
/// <summary>
/// Samurai (SAM).
/// </summary>
Samurai = 1 << 23,
/// <summary>
/// Red mage (RDM).
/// </summary>
RedMage = 1 << 24,
/// <summary>
/// Blue mage (BLM).
/// </summary>
BlueMage = 1 << 25,
/// <summary>
/// Gunbreaker (GNB).
/// </summary>
Gunbreaker = 1 << 26,
/// <summary>
/// Dancer (DNC).
/// </summary>
Dancer = 1 << 27,
}
}

View file

@ -0,0 +1,27 @@
using System;
using Dalamud.Data;
using Lumina.Excel.GeneratedSheets;
namespace Dalamud.Game.Gui.PartyFinder.Types
{
/// <summary>
/// Extensions for the <see cref="JobFlags"/> enum.
/// </summary>
public static class JobFlagsExtensions
{
/// <summary>
/// Get the actual ClassJob from the in-game sheets for this JobFlags.
/// </summary>
/// <param name="job">A JobFlags enum member.</param>
/// <param name="data">A DataManager to get the ClassJob from.</param>
/// <returns>A ClassJob if found or null if not.</returns>
public static ClassJob ClassJob(this JobFlags job, DataManager data)
{
var result = Math.Log2((double)job);
return result % 1 == 0
? data.GetExcelSheet<ClassJob>().GetRow((uint)result)
: null;
}
}
}

View file

@ -3,10 +3,11 @@ using System.Collections.Generic;
using System.Linq;
using Dalamud.Data;
using Dalamud.Game.Gui.PartyFinder.Internal;
using Dalamud.Game.Text.SeStringHandling;
using Lumina.Excel.GeneratedSheets;
namespace Dalamud.Game.Internal.Gui.Structs
namespace Dalamud.Game.Gui.PartyFinder.Types
{
/// <summary>
/// A single listing in party finder.
@ -31,7 +32,7 @@ namespace Dalamud.Game.Internal.Gui.Structs
/// <param name="listing">The interop listing data.</param>
/// <param name="dataManager">The DataManager instance.</param>
/// <param name="seStringManager">The SeStringManager instance.</param>
internal PartyFinderListing(PartyFinder.Listing listing, DataManager dataManager, SeStringManager seStringManager)
internal PartyFinderListing(PartyFinderPacketListing listing, DataManager dataManager, SeStringManager seStringManager)
{
this.objective = listing.Objective;
this.conditions = listing.Conditions;
@ -48,7 +49,7 @@ namespace Dalamud.Game.Internal.Gui.Structs
this.World = new Lazy<World>(() => dataManager.GetExcelSheet<World>().GetRow(listing.World));
this.HomeWorld = new Lazy<World>(() => dataManager.GetExcelSheet<World>().GetRow(listing.HomeWorld));
this.CurrentWorld = new Lazy<World>(() => dataManager.GetExcelSheet<World>().GetRow(listing.CurrentWorld));
this.Category = (Category)listing.Category;
this.Category = (DutyFinderCategory)listing.Category;
this.RawDuty = listing.Duty;
this.Duty = new Lazy<ContentFinderCondition>(() => dataManager.GetExcelSheet<ContentFinderCondition>().GetRow(listing.Duty));
this.DutyType = (DutyType)listing.DutyType;
@ -103,7 +104,7 @@ namespace Dalamud.Game.Internal.Gui.Structs
/// <summary>
/// Gets the Party Finder category this listing is listed under.
/// </summary>
public Category Category { get; }
public DutyFinderCategory Category { get; }
/// <summary>
/// Gets the row ID of the duty this listing is for. May be 0 for non-duty listings.
@ -154,12 +155,12 @@ namespace Dalamud.Game.Internal.Gui.Structs
/// <summary>
/// Gets the objective of this listing.
/// </summary>
public ObjectiveFlags Objective => (ObjectiveFlags)this.objective;
public DutyFinderObjectiveFlags Objective => (DutyFinderObjectiveFlags)this.objective;
/// <summary>
/// Gets the conditions of this listing.
/// </summary>
public ConditionFlags Conditions => (ConditionFlags)this.conditions;
public DutyFinderConditionFlags Conditions => (DutyFinderConditionFlags)this.conditions;
/// <summary>
/// Gets the Duty Finder settings that will be used for this listing.
@ -169,13 +170,13 @@ namespace Dalamud.Game.Internal.Gui.Structs
/// <summary>
/// Gets the loot rules that will be used for this listing.
/// </summary>
public LootRuleFlags LootRules => (LootRuleFlags)this.lootRules;
public DutyFinderLootRuleFlags LootRules => (DutyFinderLootRuleFlags)this.lootRules;
/// <summary>
/// Gets where this listing is searching. Note that this is also used for denoting alliance raid listings and one
/// player per job.
/// </summary>
public SearchAreaFlags SearchArea => (SearchAreaFlags)this.searchArea;
public DutyFinderSearchAreaFlags SearchArea => (DutyFinderSearchAreaFlags)this.searchArea;
/// <summary>
/// Gets a list of the class/job IDs that are currently present in the party.
@ -194,14 +195,14 @@ namespace Dalamud.Game.Internal.Gui.Structs
/// </summary>
/// <param name="flag">The flag to check for.</param>
/// <returns>A value indicating whether the flag is present.</returns>
public bool this[ObjectiveFlags flag] => this.objective == 0 || (this.objective & (uint)flag) > 0;
public bool this[DutyFinderObjectiveFlags flag] => this.objective == 0 || (this.objective & (uint)flag) > 0;
/// <summary>
/// Check if the given flag is present.
/// </summary>
/// <param name="flag">The flag to check for.</param>
/// <returns>A value indicating whether the flag is present.</returns>
public bool this[ConditionFlags flag] => this.conditions == 0 || (this.conditions & (uint)flag) > 0;
public bool this[DutyFinderConditionFlags flag] => this.conditions == 0 || (this.conditions & (uint)flag) > 0;
/// <summary>
/// Check if the given flag is present.
@ -215,14 +216,14 @@ namespace Dalamud.Game.Internal.Gui.Structs
/// </summary>
/// <param name="flag">The flag to check for.</param>
/// <returns>A value indicating whether the flag is present.</returns>
public bool this[LootRuleFlags flag] => this.lootRules == 0 || (this.lootRules & (uint)flag) > 0;
public bool this[DutyFinderLootRuleFlags flag] => this.lootRules == 0 || (this.lootRules & (uint)flag) > 0;
/// <summary>
/// Check if the given flag is present.
/// </summary>
/// <param name="flag">The flag to check for.</param>
/// <returns>A value indicating whether the flag is present.</returns>
public bool this[SearchAreaFlags flag] => this.searchArea == 0 || (this.searchArea & (uint)flag) > 0;
public bool this[DutyFinderSearchAreaFlags flag] => this.searchArea == 0 || (this.searchArea & (uint)flag) > 0;
#endregion

View file

@ -0,0 +1,27 @@
namespace Dalamud.Game.Gui.PartyFinder.Types
{
/// <summary>
/// This class represents additional arguments passed by the game.
/// </summary>
public class PartyFinderListingEventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="PartyFinderListingEventArgs"/> class.
/// </summary>
/// <param name="batchNumber">The batch number.</param>
internal PartyFinderListingEventArgs(int batchNumber)
{
this.BatchNumber = batchNumber;
}
/// <summary>
/// Gets the batch number.
/// </summary>
public int BatchNumber { get; }
/// <summary>
/// Gets or sets a value indicating whether the listing is visible.
/// </summary>
public bool Visible { get; set; } = true;
}
}

View file

@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace Dalamud.Game.Internal.Gui.Structs
namespace Dalamud.Game.Gui.PartyFinder.Types
{
/// <summary>
/// A player slot in a Party Finder listing.

View file

@ -1,4 +1,4 @@
namespace Dalamud.Game.Internal.Gui.Toast
namespace Dalamud.Game.Gui.Toast
{
/// <summary>
/// This class represents options that can be used with the <see cref="ToastGui"/> class for the quest toast variant.

View file

@ -1,4 +1,4 @@
namespace Dalamud.Game.Internal.Gui.Toast
namespace Dalamud.Game.Gui.Toast
{
/// <summary>
/// The alignment of native quest toast windows.

View file

@ -1,4 +1,4 @@
namespace Dalamud.Game.Internal.Gui.Toast
namespace Dalamud.Game.Gui.Toast
{
/// <summary>
/// This class represents options that can be used with the <see cref="ToastGui"/> class.

View file

@ -1,4 +1,4 @@
namespace Dalamud.Game.Internal.Gui.Toast
namespace Dalamud.Game.Gui.Toast
{
/// <summary>
/// The positioning of native toast windows.

View file

@ -1,4 +1,4 @@
namespace Dalamud.Game.Internal.Gui.Toast
namespace Dalamud.Game.Gui.Toast
{
/// <summary>
/// The speed at which native toast windows will persist.

View file

@ -2,11 +2,11 @@ using System;
using System.Collections.Generic;
using System.Text;
using Dalamud.Game.Internal.Gui.Toast;
using Dalamud.Game.Gui.Toast;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Hooking;
namespace Dalamud.Game.Internal.Gui
namespace Dalamud.Game.Gui
{
/// <summary>
/// This class facilitates interacting with and creating native toast windows.

View file

@ -1,6 +1,8 @@
using System;
namespace Dalamud.Game.Internal.Gui
using Dalamud.Game.Internal;
namespace Dalamud.Game.Gui
{
/// <summary>
/// An address resolver for the <see cref="ToastGui"/> class.

View file

@ -8,7 +8,7 @@ namespace Dalamud.Game.Internal
/// <summary>
/// This class disables anti-debug functionality in the game client.
/// </summary>
public sealed partial class AntiDebug
internal sealed partial class AntiDebug
{
private readonly byte[] nop = new byte[] { 0x31, 0xC0, 0x90, 0x90, 0x90, 0x90 };
private byte[] original;
@ -79,7 +79,7 @@ namespace Dalamud.Game.Internal
/// <summary>
/// Implementing IDisposable.
/// </summary>
public sealed partial class AntiDebug : IDisposable
internal sealed partial class AntiDebug : IDisposable
{
private bool disposed = false;

View file

@ -8,7 +8,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
namespace Dalamud.Game.Addon
namespace Dalamud.Game.Internal
{
/// <summary>
/// This class implements in-game Dalamud options in the in-game System menu.

View file

@ -1,66 +0,0 @@
using System.Runtime.InteropServices;
using FFXIVClientStructs.FFXIV.Component.GUI;
namespace Dalamud.Game.Internal.Gui.Structs
{
/// <summary>
/// Native memory representation of an FFXIV UI addon.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct Addon
{
/// <summary>
/// The name of the addon.
/// </summary>
[FieldOffset(AddonOffsets.Name)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string Name;
/// <summary>
/// Various flags that can be set on the addon.
/// </summary>
/// <remarks>
/// This is a bitfield.
/// </remarks>
[FieldOffset(AddonOffsets.Flags)]
public byte Flags;
/// <summary>
/// The X position of the addon on screen.
/// </summary>
[FieldOffset(AddonOffsets.X)]
public short X;
/// <summary>
/// The Y position of the addon on screen.
/// </summary>
[FieldOffset(AddonOffsets.Y)]
public short Y;
/// <summary>
/// The scale of the addon.
/// </summary>
[FieldOffset(AddonOffsets.Scale)]
public float Scale;
/// <summary>
/// The root node of the addon's node tree.
/// </summary>
[FieldOffset(AddonOffsets.RootNode)]
public unsafe AtkResNode* RootNode;
}
/// <summary>
/// Memory offsets for the <see cref="Addon"/> type.
/// </summary>
public static class AddonOffsets
{
public const int Name = 0x8;
public const int RootNode = 0xC8;
public const int Flags = 0x182;
public const int X = 0x1BC;
public const int Y = 0x1BE;
public const int Scale = 0x1AC;
}
}

View file

@ -1,129 +0,0 @@
using System.Linq;
using System.Runtime.InteropServices;
namespace Dalamud.Game.Internal.Gui.Structs
{
/// <summary>
/// PartyFinder related network structs and static constants.
/// </summary>
internal static class PartyFinder
{
/// <summary>
/// The structure of the PartyFinder packet.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal readonly struct Packet
{
internal readonly int BatchNumber;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
private readonly byte[] padding1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
internal readonly Listing[] Listings;
}
/// <summary>
/// The structure of an individual listing within a packet.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal readonly struct Listing
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
private readonly byte[] header1;
internal readonly uint Id;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
private readonly byte[] header2;
internal readonly uint ContentIdLower;
private readonly ushort unknownShort1;
private readonly ushort unknownShort2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
private readonly byte[] header3;
internal readonly byte Category;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
private readonly byte[] header4;
internal readonly ushort Duty;
internal readonly byte DutyType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
private readonly byte[] header5;
internal readonly ushort World;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
private readonly byte[] header6;
internal readonly byte Objective;
internal readonly byte BeginnersWelcome;
internal readonly byte Conditions;
internal readonly byte DutyFinderSettings;
internal readonly byte LootRules;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
private readonly byte[] header7; // all zero in every pf I've examined
private readonly uint lastPatchHotfixTimestamp; // last time the servers were restarted?
internal readonly ushort SecondsRemaining;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
private readonly byte[] header8; // 00 00 01 00 00 00 in every pf I've examined
internal readonly ushort MinimumItemLevel;
internal readonly ushort HomeWorld;
internal readonly ushort CurrentWorld;
private readonly byte header9;
internal readonly byte NumSlots;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
private readonly byte[] header10;
internal readonly byte SearchArea;
private readonly byte header11;
internal readonly byte NumParties;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
private readonly byte[] header12; // 00 00 00 always. maybe numParties is a u32?
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
internal readonly uint[] Slots;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
internal readonly byte[] JobsPresent;
// Note that ByValTStr will not work here because the strings are UTF-8 and there's only a CharSet for UTF-16 in C#.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
internal readonly byte[] Name;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 192)]
internal readonly byte[] Description;
internal bool IsNull()
{
// a valid party finder must have at least one slot set
return this.Slots.All(slot => slot == 0);
}
}
/// <summary>
/// PartyFinder packet constants.
/// </summary>
public static class PacketInfo
{
/// <summary>
/// The size of the PartyFinder packet.
/// </summary>
public static readonly int PacketSize = Marshal.SizeOf<Packet>();
}
}
}

View file

@ -1,397 +0,0 @@
using System;
using Dalamud.Data;
using Lumina.Excel.GeneratedSheets;
namespace Dalamud.Game.Internal.Gui.Structs
{
/// <summary>
/// Search area flags for the <see cref="PartyFinder"/> class.
/// </summary>
[Flags]
public enum SearchAreaFlags : uint
{
/// <summary>
/// Datacenter.
/// </summary>
DataCentre = 1 << 0,
/// <summary>
/// Private.
/// </summary>
Private = 1 << 1,
/// <summary>
/// Alliance raid.
/// </summary>
AllianceRaid = 1 << 2,
/// <summary>
/// World.
/// </summary>
World = 1 << 3,
/// <summary>
/// One player per job.
/// </summary>
OnePlayerPerJob = 1 << 5,
}
/// <summary>
/// Job flags for the <see cref="PartyFinder"/> class.
/// </summary>
[Flags]
public enum JobFlags
{
/// <summary>
/// Gladiator (GLD).
/// </summary>
Gladiator = 1 << 1,
/// <summary>
/// Pugilist (PGL).
/// </summary>
Pugilist = 1 << 2,
/// <summary>
/// Marauder (MRD).
/// </summary>
Marauder = 1 << 3,
/// <summary>
/// Lancer (LNC).
/// </summary>
Lancer = 1 << 4,
/// <summary>
/// Archer (ARC).
/// </summary>
Archer = 1 << 5,
/// <summary>
/// Conjurer (CNJ).
/// </summary>
Conjurer = 1 << 6,
/// <summary>
/// Thaumaturge (THM).
/// </summary>
Thaumaturge = 1 << 7,
/// <summary>
/// Paladin (PLD).
/// </summary>
Paladin = 1 << 8,
/// <summary>
/// Monk (MNK).
/// </summary>
Monk = 1 << 9,
/// <summary>
/// Warrior (WAR).
/// </summary>
Warrior = 1 << 10,
/// <summary>
/// Dragoon (DRG).
/// </summary>
Dragoon = 1 << 11,
/// <summary>
/// Bard (BRD).
/// </summary>
Bard = 1 << 12,
/// <summary>
/// White mage (WHM).
/// </summary>
WhiteMage = 1 << 13,
/// <summary>
/// Black mage (BLM).
/// </summary>
BlackMage = 1 << 14,
/// <summary>
/// Arcanist (ACN).
/// </summary>
Arcanist = 1 << 15,
/// <summary>
/// Summoner (SMN).
/// </summary>
Summoner = 1 << 16,
/// <summary>
/// Scholar (SCH).
/// </summary>
Scholar = 1 << 17,
/// <summary>
/// Rogue (ROG).
/// </summary>
Rogue = 1 << 18,
/// <summary>
/// Ninja (NIN).
/// </summary>
Ninja = 1 << 19,
/// <summary>
/// Machinist (MCH).
/// </summary>
Machinist = 1 << 20,
/// <summary>
/// Dark Knight (DRK).
/// </summary>
DarkKnight = 1 << 21,
/// <summary>
/// Astrologian (AST).
/// </summary>
Astrologian = 1 << 22,
/// <summary>
/// Samurai (SAM).
/// </summary>
Samurai = 1 << 23,
/// <summary>
/// Red mage (RDM).
/// </summary>
RedMage = 1 << 24,
/// <summary>
/// Blue mage (BLM).
/// </summary>
BlueMage = 1 << 25,
/// <summary>
/// Gunbreaker (GNB).
/// </summary>
Gunbreaker = 1 << 26,
/// <summary>
/// Dancer (DNC).
/// </summary>
Dancer = 1 << 27,
}
/// <summary>
/// Objective flags for the <see cref="PartyFinder"/> class.
/// </summary>
[Flags]
public enum ObjectiveFlags : uint
{
/// <summary>
/// No objective.
/// </summary>
None = 0,
/// <summary>
/// The duty completion objective.
/// </summary>
DutyCompletion = 1,
/// <summary>
/// The practice objective.
/// </summary>
Practice = 2,
/// <summary>
/// The loot objective.
/// </summary>
Loot = 4,
}
/// <summary>
/// Condition flags for the <see cref="PartyFinder"/> class.
/// </summary>
[Flags]
public enum ConditionFlags : uint
{
/// <summary>
/// No duty condition.
/// </summary>
None = 1,
/// <summary>
/// The duty complete condition.
/// </summary>
DutyComplete = 2,
/// <summary>
/// The duty incomplete condition.
/// </summary>
DutyIncomplete = 4,
}
/// <summary>
/// Duty finder settings flags for the <see cref="PartyFinder"/> class.
/// </summary>
[Flags]
public enum DutyFinderSettingsFlags : uint
{
/// <summary>
/// No duty finder settings.
/// </summary>
None = 0,
/// <summary>
/// The undersized party setting.
/// </summary>
UndersizedParty = 1 << 0,
/// <summary>
/// The minimum item level setting.
/// </summary>
MinimumItemLevel = 1 << 1,
/// <summary>
/// The silence echo setting.
/// </summary>
SilenceEcho = 1 << 2,
}
/// <summary>
/// Loot rule flags for the <see cref="PartyFinder"/> class.
/// </summary>
[Flags]
public enum LootRuleFlags : uint
{
/// <summary>
/// No loot rules.
/// </summary>
None = 0,
/// <summary>
/// The greed only rule.
/// </summary>
GreedOnly = 1,
/// <summary>
/// The lootmaster rule.
/// </summary>
Lootmaster = 2,
}
/// <summary>
/// Category flags for the <see cref="PartyFinder"/> class.
/// </summary>
public enum Category
{
/// <summary>
/// The duty category.
/// </summary>
Duty = 0,
/// <summary>
/// The quest battle category.
/// </summary>
QuestBattles = 1 << 0,
/// <summary>
/// The fate category.
/// </summary>
Fates = 1 << 1,
/// <summary>
/// The treasure hunt category.
/// </summary>
TreasureHunt = 1 << 2,
/// <summary>
/// The hunt category.
/// </summary>
TheHunt = 1 << 3,
/// <summary>
/// The gathering forays category.
/// </summary>
GatheringForays = 1 << 4,
/// <summary>
/// The deep dungeons category.
/// </summary>
DeepDungeons = 1 << 5,
/// <summary>
/// The adventuring forays category.
/// </summary>
AdventuringForays = 1 << 6,
}
/// <summary>
/// Duty type flags for the <see cref="PartyFinder"/> class.
/// </summary>
public enum DutyType
{
/// <summary>
/// No duty type.
/// </summary>
Other = 0,
/// <summary>
/// The roulette duty type.
/// </summary>
Roulette = 1 << 0,
/// <summary>
/// The normal duty type.
/// </summary>
Normal = 1 << 1,
}
/// <summary>
/// Extensions for the <see cref="JobFlags"/> enum.
/// </summary>
public static class JobFlagsExt
{
/// <summary>
/// Get the actual ClassJob from the in-game sheets for this JobFlags.
/// </summary>
/// <param name="job">A JobFlags enum member.</param>
/// <param name="data">A DataManager to get the ClassJob from.</param>
/// <returns>A ClassJob if found or null if not.</returns>
public static ClassJob ClassJob(this JobFlags job, DataManager data)
{
var jobs = data.GetExcelSheet<ClassJob>();
uint? row = job switch
{
JobFlags.Gladiator => 1,
JobFlags.Pugilist => 2,
JobFlags.Marauder => 3,
JobFlags.Lancer => 4,
JobFlags.Archer => 5,
JobFlags.Conjurer => 6,
JobFlags.Thaumaturge => 7,
JobFlags.Paladin => 19,
JobFlags.Monk => 20,
JobFlags.Warrior => 21,
JobFlags.Dragoon => 22,
JobFlags.Bard => 23,
JobFlags.WhiteMage => 24,
JobFlags.BlackMage => 25,
JobFlags.Arcanist => 26,
JobFlags.Summoner => 27,
JobFlags.Scholar => 28,
JobFlags.Rogue => 29,
JobFlags.Ninja => 30,
JobFlags.Machinist => 31,
JobFlags.DarkKnight => 32,
JobFlags.Astrologian => 33,
JobFlags.Samurai => 34,
JobFlags.RedMage => 35,
JobFlags.BlueMage => 36,
JobFlags.Gunbreaker => 37,
JobFlags.Dancer => 38,
_ => null,
};
return row == null ? null : jobs.GetRow((uint)row);
}
}
}

View file

@ -1,159 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Dalamud.Hooking;
using Serilog;
namespace Dalamud.Game.Internal.File
{
/// <summary>
/// This class facilitates modifying how the game loads resources from disk.
/// </summary>
public class ResourceManager
{
private readonly Dalamud dalamud;
private readonly ResourceManagerAddressResolver address;
private readonly Hook<GetResourceAsyncDelegate> getResourceAsyncHook;
private readonly Hook<GetResourceSyncDelegate> getResourceSyncHook;
private Dictionary<IntPtr, ResourceHandleHookInfo> resourceHookMap = new();
/// <summary>
/// Initializes a new instance of the <see cref="ResourceManager"/> class.
/// </summary>
/// <param name="dalamud">The Dalamud instance.</param>
/// <param name="scanner">The SigScanner instance.</param>
internal ResourceManager(Dalamud dalamud, SigScanner scanner)
{
this.dalamud = dalamud;
this.address = new ResourceManagerAddressResolver();
this.address.Setup(scanner);
Log.Verbose("===== R E S O U R C E M A N A G E R =====");
Log.Verbose("GetResourceAsync address {GetResourceAsync}", this.address.GetResourceAsync);
Log.Verbose("GetResourceSync address {GetResourceSync}", this.address.GetResourceSync);
this.getResourceAsyncHook = new Hook<GetResourceAsyncDelegate>(this.address.GetResourceAsync, this.GetResourceAsyncDetour);
this.getResourceSyncHook = new Hook<GetResourceSyncDelegate>(this.address.GetResourceSync, this.GetResourceSyncDetour);
}
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate IntPtr GetResourceAsyncDelegate(IntPtr manager, IntPtr a2, IntPtr a3, IntPtr a4, IntPtr pathPtr, IntPtr a6, byte a7);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate IntPtr GetResourceSyncDelegate(IntPtr manager, IntPtr a2, IntPtr a3, IntPtr a4, IntPtr pathPtr, IntPtr a6);
/// <summary>
/// Check if a filepath has any invalid characters.
/// </summary>
/// <param name="path">The filepath to check.</param>
/// <returns>A value indicating whether the filepath is safe to use.</returns>
public static bool FilePathHasInvalidChars(string path)
{
return !string.IsNullOrEmpty(path) && path.IndexOfAny(Path.GetInvalidPathChars()) >= 0;
}
/// <summary>
/// Enable this module.
/// </summary>
public void Enable()
{
this.getResourceAsyncHook.Enable();
this.getResourceSyncHook.Enable();
}
/// <summary>
/// Dispose of managed and unmanaged resources.
/// </summary>
public void Dispose()
{
this.getResourceAsyncHook.Dispose();
this.getResourceSyncHook.Dispose();
}
private IntPtr GetResourceAsyncDetour(IntPtr manager, IntPtr a2, IntPtr a3, IntPtr a4, IntPtr pathPtr, IntPtr a6, byte a7)
{
try
{
var path = Marshal.PtrToStringAnsi(pathPtr);
var resourceHandle = this.getResourceAsyncHook.Original(manager, a2, a3, a4, IntPtr.Zero, a6, a7);
// var resourceHandle = IntPtr.Zero;
Log.Verbose("GetResourceAsync CALL - this:{0} a2:{1} a3:{2} a4:{3} a5:{4} a6:{5} a7:{6} => RET:{7}", manager, a2, a3, a4, pathPtr, a6, a7, resourceHandle);
Log.Verbose($"->{path}");
this.HandleGetResourceHookAcquire(resourceHandle, path);
return resourceHandle;
}
catch (Exception ex)
{
Log.Error(ex, "Exception on ReadResourceAsync hook.");
return this.getResourceAsyncHook.Original(manager, a2, a3, a4, pathPtr, a6, a7);
}
}
private IntPtr GetResourceSyncDetour(IntPtr manager, IntPtr a2, IntPtr a3, IntPtr a4, IntPtr pathPtr, IntPtr a6)
{
try
{
var resourceHandle = this.getResourceSyncHook.Original(manager, a2, a3, a4, pathPtr, a6);
Log.Verbose("GetResourceSync CALL - this:{0} a2:{1} a3:{2} a4:{3} a5:{4} a6:{5} => RET:{6}", manager, a2, a3, a4, pathPtr, a6, resourceHandle);
var path = Marshal.PtrToStringAnsi(pathPtr);
Log.Verbose($"->{path}");
this.HandleGetResourceHookAcquire(resourceHandle, path);
return resourceHandle;
}
catch (Exception ex)
{
Log.Error(ex, "Exception on ReadResourceSync hook.");
return this.getResourceSyncHook.Original(manager, a2, a3, a4, pathPtr, a6);
}
}
private void HandleGetResourceHookAcquire(IntPtr handlePtr, string path)
{
if (FilePathHasInvalidChars(path))
return;
if (this.resourceHookMap.ContainsKey(handlePtr))
{
Log.Verbose($"-> Handle {handlePtr.ToInt64():X}({path}) was cached!");
return;
}
var hookInfo = new ResourceHandleHookInfo
{
Path = path,
};
var hookPath = Path.Combine(this.dalamud.StartInfo.WorkingDirectory, "ResourceHook", path);
if (System.IO.File.Exists(hookPath))
{
hookInfo.DetourFile = new FileStream(hookPath, FileMode.Open);
Log.Verbose("-> Added resource hook detour at {0}", hookPath);
}
this.resourceHookMap.Add(handlePtr, hookInfo);
}
private class ResourceHandleHookInfo
{
public string Path { get; set; }
public Stream DetourFile { get; set; }
}
}
}

View file

@ -1,28 +0,0 @@
using System;
namespace Dalamud.Game.Internal.File
{
/// <summary>
/// The address resolver for the <see cref="ResourceManager"/> class.
/// </summary>
internal class ResourceManagerAddressResolver : BaseAddressResolver
{
/// <summary>
/// Gets the address of the GetResourceAsync method.
/// </summary>
public IntPtr GetResourceAsync { get; private set; }
/// <summary>
/// Gets the address of the GetResourceSync method.
/// </summary>
public IntPtr GetResourceSync { get; private set; }
/// <inheritdoc/>
protected override void Setup64Bit(SigScanner sig)
{
this.GetResourceAsync = sig.ScanText("48 89 5C 24 08 48 89 54 24 10 57 48 83 EC 20 B8 03 00 00 00 48 8B F9 86 82 A1 00 00 00 48 8B 5C 24 38 B8 01 00 00 00 87 83 90 00 00 00 85 C0 74");
this.GetResourceSync = sig.ScanText("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 54 41 55 41 56 41 57 48 83 EC 30 48 8B F9 49 8B E9 48 83 C1 30 4D 8B F0 4C 8B EA FF 15 CE F6");
// ReadResourceSync = sig.ScanText("48 89 74 24 18 57 48 83 EC 50 8B F2 49 8B F8 41 0F B7 50 02 8B CE E8 ?? ?? 7A FF 0F B7 57 02 8D 42 89 3D 5F 02 00 00 0F 87 60 01 00 00 4C 8D 05");
}
}
}

View file

@ -2,7 +2,7 @@ using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Dalamud.Game.Internal.Libc
namespace Dalamud.Game.Libc
{
/// <summary>
/// This class handles creating cstrings utilizing native game methods.

View file

@ -1,6 +1,8 @@
using System;
namespace Dalamud.Game.Internal.Libc
using Dalamud.Game.Internal;
namespace Dalamud.Game.Libc
{
/// <summary>
/// The address resolver for the <see cref="LibcFunction"/> class.

View file

@ -1,7 +1,7 @@
using System;
using System.Runtime.InteropServices;
namespace Dalamud.Game.Internal.Libc
namespace Dalamud.Game.Libc
{
/// <summary>
/// An address wrapper around the <see cref="StdString"/> class.

View file

@ -2,7 +2,7 @@ using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Dalamud.Game.Internal.Libc
namespace Dalamud.Game.Libc
{
/// <summary>
/// Interation with std::string.

View file

@ -6,18 +6,13 @@ using Dalamud.Game.Network;
using Dalamud.Hooking;
using Serilog;
namespace Dalamud.Game.Internal.Network
namespace Dalamud.Game.Network
{
/// <summary>
/// This class handles interacting with game network events.
/// </summary>
public sealed class GameNetwork : IDisposable
{
/// <summary>
/// Event that is called when a network message is sent/received.
/// </summary>
public OnNetworkMessageDelegate OnNetworkMessage;
private readonly GameNetworkAddressResolver address;
private readonly Hook<ProcessZonePacketDownDelegate> processZonePacketDownHook;
private readonly Hook<ProcessZonePacketUpDelegate> processZonePacketUpHook;
@ -58,6 +53,11 @@ namespace Dalamud.Game.Internal.Network
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate byte ProcessZonePacketUpDelegate(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4);
/// <summary>
/// Event that is called when a network message is sent/received.
/// </summary>
public event OnNetworkMessageDelegate OnNetworkMessage;
/// <summary>
/// Enable this module.
/// </summary>

View file

@ -1,6 +1,8 @@
using System;
namespace Dalamud.Game.Internal.Network
using Dalamud.Game.Internal;
namespace Dalamud.Game.Network
{
/// <summary>
/// The address resolver for the <see cref="GameNetwork"/> class.

View file

@ -23,6 +23,19 @@ namespace Dalamud.Game
/// </summary>
public float Y;
/// <summary>
/// Initializes a new instance of the <see cref="Position3"/> struct.
/// </summary>
/// <param name="x">The X position.</param>
/// <param name="z">The Z position.</param>
/// <param name="y">The Y position.</param>
public Position3(float x, float z, float y)
{
this.X = x;
this.Z = z;
this.Y = y;
}
/// <summary>
/// Convert this Position3 to a System.Numerics.Vector3.
/// </summary>

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Dalamud.Data;
@ -21,15 +22,15 @@ namespace Dalamud.Game.Text.SeStringHandling
/// </summary>
public abstract partial class Payload
{
/// <summary>
/// The Lumina instance to use for any necessary data lookups.
/// </summary>
public DataManager DataResolver;
// private for now, since subclasses shouldn't interact with this
// private for now, since subclasses shouldn't interact with this.
// To force-invalidate it, Dirty can be set to true
private byte[] encodedData;
/// <summary>
/// Gets or sets the Lumina instance to use for any necessary data lookups.
/// </summary>
public DataManager DataResolver { get; set; }
/// <summary>
/// Gets the type of this payload.
/// </summary>
@ -233,11 +234,13 @@ namespace Dalamud.Game.Text.SeStringHandling
/// <summary>
/// The start byte of a payload.
/// </summary>
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "This is prefered.")]
protected const byte START_BYTE = 0x02;
/// <summary>
/// The end byte of a payload.
/// </summary>
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "This is prefered.")]
protected const byte END_BYTE = 0x03;
/// <summary>

View file

@ -19,17 +19,6 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
this.Icon = icon;
}
/// <summary>
/// Initializes a new instance of the <see cref="IconPayload"/> class.
/// Create a Icon payload for the specified icon.
/// </summary>
/// <param name="iconIndex">Index of the icon.</param>
[Obsolete("IconPayload(uint) is deprecated, please use IconPayload(BitmapFontIcon).")]
public IconPayload(uint iconIndex)
: this((BitmapFontIcon)iconIndex)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="IconPayload"/> class.
/// Create a Icon payload for the specified icon.
@ -41,12 +30,6 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
/// <inheritdoc/>
public override PayloadType Type => PayloadType.Icon;
/// <summary>
/// Gets the index of the icon.
/// </summary>
[Obsolete("Use IconPayload.Icon")]
public uint IconIndex => (uint)this.Icon;
/// <summary>
/// Gets or sets the icon the payload represents.
/// </summary>

View file

@ -222,7 +222,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
var c = scale / 100.0f;
var scaledPos = pos * c / 1000.0f;
return ((41.0f / c) * ((scaledPos + 1024.0f) / 2048.0f)) + 1.0f;
return (41.0f / c * ((scaledPos + 1024.0f) / 2048.0f)) + 1.0f;
}
// Created as the inverse of ConvertRawPositionToMapCoordinate(), since no one seemed to have a version of that
@ -230,7 +230,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
{
var c = scale / 100.0f;
var scaledPos = ((((pos - 1.0f) * c / 41.0f) * 2048.0f) - 1024.0f) / c;
var scaledPos = (((pos - 1.0f) * c / 41.0f * 2048.0f) - 1024.0f) / c;
scaledPos *= 1000.0f;
return (int)scaledPos;

View file

@ -84,12 +84,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
/// <inheritdoc/>
public override int GetHashCode()
{
// Generated random values.
var hashCode = 1216194372;
hashCode = (hashCode * -1521134295) + this.Type.GetHashCode();
hashCode = (hashCode * -1521134295) + this.chunkType.GetHashCode();
hashCode = (hashCode * -1521134295) + EqualityComparer<byte[]>.Default.GetHashCode(this.data);
return hashCode;
return HashCode.Combine(this.Type, this.chunkType, this.data);
}
/// <inheritdoc/>

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
@ -67,7 +68,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads
// this may change or go away
if (string.IsNullOrEmpty(this.text))
{
return new byte[] { };
return Array.Empty<byte>();
}
return Encoding.UTF8.GetBytes(this.text);

View file

@ -1,6 +1,3 @@
using System;
using System.Linq;
namespace Dalamud.Game.Text
{
/// <summary>
@ -237,77 +234,4 @@ namespace Dalamud.Game.Text
[XivChatTypeInfo("Crossworld Linkshell 8", "cw8", 0xFF1E90FF)]
CrossLinkShell8 = 107,
}
/// <summary>
/// Extension methods for the <see cref="XivChatType"/> type.
/// </summary>
public static class XivChatTypeExtensions
{
/// <summary>
/// Get the InfoAttribute associated with this chat type.
/// </summary>
/// <param name="chatType">The chat type.</param>
/// <returns>The info attribute.</returns>
public static XivChatTypeInfoAttribute GetDetails(this XivChatType chatType)
{
return chatType.GetAttribute<XivChatTypeInfoAttribute>();
}
}
/// <summary>
/// Storage for relevant information associated with the chat type.
/// </summary>
public class XivChatTypeInfoAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="XivChatTypeInfoAttribute"/> class.
/// </summary>
/// <param name="fancyName">The fancy name.</param>
/// <param name="slug">The name slug.</param>
/// <param name="defaultColor">The default color.</param>
internal XivChatTypeInfoAttribute(string fancyName, string slug, uint defaultColor)
{
this.FancyName = fancyName;
this.Slug = slug;
this.DefaultColor = defaultColor;
}
/// <summary>
/// Gets the "fancy" name of the type.
/// </summary>
public string FancyName { get; }
/// <summary>
/// Gets the type name slug or short-form.
/// </summary>
public string Slug { get; }
/// <summary>
/// Gets the type default color.
/// </summary>
public uint DefaultColor { get; }
}
/// <summary>
/// Extension methods for enums.
/// </summary>
public static class EnumExtensions
{
/// <summary>
/// Gets an attribute on an enum.
/// </summary>
/// <typeparam name="TAttribute">The type of attribute to get.</typeparam>
/// <param name="value">The enum value that has an attached attribute.</param>
/// <returns>The attached attribute, if any.</returns>
public static TAttribute GetAttribute<TAttribute>(this Enum value)
where TAttribute : Attribute
{
var type = value.GetType();
var name = Enum.GetName(type, value);
return type.GetField(name) // I prefer to get attributes this way
.GetCustomAttributes(false)
.OfType<TAttribute>()
.SingleOrDefault();
}
}
}

View file

@ -0,0 +1,20 @@
using Dalamud.Utility;
namespace Dalamud.Game.Text
{
/// <summary>
/// Extension methods for the <see cref="XivChatType"/> type.
/// </summary>
public static class XivChatTypeExtensions
{
/// <summary>
/// Get the InfoAttribute associated with this chat type.
/// </summary>
/// <param name="chatType">The chat type.</param>
/// <returns>The info attribute.</returns>
public static XivChatTypeInfoAttribute GetDetails(this XivChatType chatType)
{
return chatType.GetAttribute<XivChatTypeInfoAttribute>();
}
}
}

View file

@ -0,0 +1,39 @@
using System;
namespace Dalamud.Game.Text
{
/// <summary>
/// Storage for relevant information associated with the chat type.
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class XivChatTypeInfoAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="XivChatTypeInfoAttribute"/> class.
/// </summary>
/// <param name="fancyName">The fancy name.</param>
/// <param name="slug">The name slug.</param>
/// <param name="defaultColor">The default color.</param>
internal XivChatTypeInfoAttribute(string fancyName, string slug, uint defaultColor)
{
this.FancyName = fancyName;
this.Slug = slug;
this.DefaultColor = defaultColor;
}
/// <summary>
/// Gets the "fancy" name of the type.
/// </summary>
public string FancyName { get; }
/// <summary>
/// Gets the type name slug or short-form.
/// </summary>
public string Slug { get; }
/// <summary>
/// Gets the type default color.
/// </summary>
public uint DefaultColor { get; }
}
}

View file

@ -67,7 +67,7 @@ namespace Dalamud.Hooking.Internal
internal Delegate Delegate { get; }
/// <summary>
/// Gets the hooked assembly.
/// Gets the assembly implementing the hook.
/// </summary>
internal Assembly Assembly { get; }
}

View file

@ -0,0 +1,30 @@
// Font-Awesome - Version 5.0.9
namespace Dalamud.Interface
{
/// <summary>
/// Extension methods for <see cref="FontAwesomeIcon"/>.
/// </summary>
public static class FontAwesomeExtensions
{
/// <summary>
/// Convert the FontAwesomeIcon to a <see cref="char"/> type.
/// </summary>
/// <param name="icon">The icon to convert.</param>
/// <returns>The converted icon.</returns>
public static char ToIconChar(this FontAwesomeIcon icon)
{
return (char)icon;
}
/// <summary>
/// Conver the FontAwesomeIcon to a <see cref="string"/> type.
/// </summary>
/// <param name="icon">The icon to convert.</param>
/// <returns>The converted icon.</returns>
public static string ToIconString(this FontAwesomeIcon icon)
{
return string.Empty + (char)icon;
}
}
}

View file

@ -7047,30 +7047,4 @@ namespace Dalamud.Interface
/// </summary>
Zhihu = 0xF63F,
}
/// <summary>
/// Extension methods for <see cref="FontAwesomeIcon"/>.
/// </summary>
public static class FontAwesomeExtensions
{
/// <summary>
/// Convert the FontAwesomeIcon to a <see cref="char"/> type.
/// </summary>
/// <param name="icon">The icon to convert.</param>
/// <returns>The converted icon.</returns>
public static char ToIconChar(this FontAwesomeIcon icon)
{
return (char)icon;
}
/// <summary>
/// Conver the FontAwesomeIcon to a <see cref="string"/> type.
/// </summary>
/// <param name="icon">The icon to convert.</param>
/// <returns>The converted icon.</returns>
public static string ToIconString(this FontAwesomeIcon icon)
{
return string.Empty + (char)icon;
}
}
}

View file

@ -242,7 +242,7 @@ namespace Dalamud.Interface.Internal
if (string.IsNullOrEmpty(arguments))
this.dalamud.DalamudUi.ToggleDataWindow();
else
this.dalamud.DalamudUi.ToggleDataWindow(arguments);
this.dalamud.DalamudUi.OpenDataWindow(arguments);
}
private void OnOpenLog(string command, string arguments)

View file

@ -9,6 +9,7 @@ using Dalamud.Interface.Windowing;
using Dalamud.Logging;
using Dalamud.Logging.Internal;
using Dalamud.Plugin.Internal;
using Dalamud.Utility;
using ImGuiNET;
using Serilog.Events;

View file

@ -8,9 +8,11 @@ using System.Threading;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.GamePad;
using Dalamud.Game.Internal.DXGI;
using Dalamud.Hooking;
using Dalamud.Hooking.Internal;
using Dalamud.Utility;
using ImGuiNET;
using ImGuiScene;
using Serilog;
@ -322,9 +324,7 @@ namespace Dalamud.Interface.Internal
private static void ShowFontError(string path)
{
Util.Fatal(
$"One or more files required by XIVLauncher were not found.\nPlease restart and report this error if it occurs again.\n\n{path}",
"Error");
Util.Fatal($"One or more files required by XIVLauncher were not found.\nPlease restart and report this error if it occurs again.\n\n{path}", "Error");
}
private IntPtr PresentDetour(IntPtr swapChain, uint syncInterval, uint presentFlags)

View file

@ -83,7 +83,7 @@ namespace Dalamud.Interface.Internal.Scratchpad
{
var script = CSharpScript.Create(code, options);
var pi = new DalamudPluginInterface(this.dalamud, "Scratch-" + doc.Id, null, PluginLoadReason.Unknown);
var pi = new DalamudPluginInterface(this.dalamud, "Scratch-" + doc.Id, PluginLoadReason.Unknown);
var plugin = script.ContinueWith<IDalamudPlugin>("return new ScratchPlugin() as IDalamudPlugin;")
.RunAsync().GetAwaiter().GetResult().ReturnValue;

View file

@ -131,7 +131,7 @@ public class ScratchPlugin : IDalamudPlugin {
case ParseContext.Dispose:
break;
default:
throw new ArgumentOutOfRangeException();
throw new ArgumentOutOfRangeException(paramName: nameof(input));
}
ctx = ParseContext.None;
@ -156,7 +156,7 @@ public class ScratchPlugin : IDalamudPlugin {
disposeBody += line + "\n";
break;
default:
throw new ArgumentOutOfRangeException();
throw new ArgumentOutOfRangeException(paramName: nameof(input));
}
}

View file

@ -1,6 +1,7 @@
using System.Diagnostics;
using Dalamud.Interface.Windowing;
using Dalamud.Utility;
using ImGuiNET;
namespace Dalamud.Interface.Internal.Windows

View file

@ -4,17 +4,20 @@ using System.Dynamic;
using System.Linq;
using System.Numerics;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Actors.Types;
using Dalamud.Game.ClientState.Actors.Types.NonPlayer;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.GamePad;
using Dalamud.Game.ClientState.Structs.JobGauge;
using Dalamud.Game.Internal;
using Dalamud.Game.Gui.Addons;
using Dalamud.Game.Gui.Toast;
using Dalamud.Game.Internal.Gui;
using Dalamud.Game.Internal.Gui.Addon;
using Dalamud.Game.Internal.Gui.Toast;
using Dalamud.Game.Text;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using Dalamud.Utility;
using ImGuiNET;
using ImGuiScene;
using Newtonsoft.Json;
@ -117,6 +120,17 @@ namespace Dalamud.Interface.Internal.Windows
Gamepad,
}
/// <inheritdoc/>
public override void OnOpen()
{
}
/// <inheritdoc/>
public override void OnClose()
{
this.resultAddon = null;
}
/// <summary>
/// Set the DataKind dropdown menu.
/// </summary>
@ -303,11 +317,11 @@ namespace Dalamud.Interface.Internal.Windows
foreach (var valueTuple in debugScannedValue.Value)
{
ImGui.TextUnformatted(
$" {valueTuple.Item1} - 0x{valueTuple.Item2.ToInt64():x}");
$" {valueTuple.ClassName} - 0x{valueTuple.Address.ToInt64():x}");
ImGui.SameLine();
if (ImGui.Button($"C##copyAddress{this.copyButtonIndex++}"))
ImGui.SetClipboardText(valueTuple.Item2.ToInt64().ToString("x"));
ImGui.SetClipboardText(valueTuple.Address.ToInt64().ToString("x"));
}
}
}
@ -486,8 +500,8 @@ namespace Dalamud.Interface.Internal.Windows
private void DrawPluginIPC()
{
#pragma warning disable CS0618 // Type or member is obsolete
var i1 = new DalamudPluginInterface(this.dalamud, "DalamudTestSub", null, PluginLoadReason.Unknown);
var i2 = new DalamudPluginInterface(this.dalamud, "DalamudTestPub", null, PluginLoadReason.Unknown);
var i1 = new DalamudPluginInterface(this.dalamud, "DalamudTestSub", PluginLoadReason.Unknown);
var i2 = new DalamudPluginInterface(this.dalamud, "DalamudTestPub", PluginLoadReason.Unknown);
if (ImGui.Button("Add test sub"))
{
@ -579,9 +593,7 @@ namespace Dalamud.Interface.Internal.Windows
if (ImGui.Button("Get Addon"))
{
this.resultAddon =
this.dalamud.Framework.Gui.GetAddonByName(
this.inputAddonName, this.inputAddonIndex);
this.resultAddon = this.dalamud.Framework.Gui.GetAddonByName(this.inputAddonName, this.inputAddonIndex);
}
if (ImGui.Button("Find Agent"))
@ -589,14 +601,12 @@ namespace Dalamud.Interface.Internal.Windows
if (this.resultAddon != null)
{
ImGui.TextUnformatted(
$"{this.resultAddon.Name} - 0x{this.resultAddon.Address.ToInt64():x}\n v:{this.resultAddon.Visible} x:{this.resultAddon.X} y:{this.resultAddon.Y} s:{this.resultAddon.Scale}, w:{this.resultAddon.Width}, h:{this.resultAddon.Height}");
ImGui.TextUnformatted($"{this.resultAddon.Name} - 0x{this.resultAddon.Address.ToInt64():x}\n v:{this.resultAddon.Visible} x:{this.resultAddon.X} y:{this.resultAddon.Y} s:{this.resultAddon.Scale}, w:{this.resultAddon.Width}, h:{this.resultAddon.Height}");
}
if (this.findAgentInterfacePtr != IntPtr.Zero)
{
ImGui.TextUnformatted(
$"Agent: 0x{this.findAgentInterfacePtr.ToInt64():x}");
ImGui.TextUnformatted($"Agent: 0x{this.findAgentInterfacePtr.ToInt64():x}");
ImGui.SameLine();
if (ImGui.Button("C"))
@ -807,13 +817,13 @@ namespace Dalamud.Interface.Internal.Windows
$"R3 {resolve(GamepadButtons.R3)} ");
}
#if DEBUG
ImGui.Text($"GamepadInput 0x{this.dalamud.ClientState.GamepadState.GamepadInput.ToInt64():X}");
ImGui.Text($"GamepadInput 0x{this.dalamud.ClientState.GamepadState.GamepadInputAddress.ToInt64():X}");
if (ImGui.IsItemHovered())
ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
if (ImGui.IsItemClicked())
ImGui.SetClipboardText($"0x{this.dalamud.ClientState.GamepadState.GamepadInput.ToInt64():X}");
ImGui.SetClipboardText($"0x{this.dalamud.ClientState.GamepadState.GamepadInputAddress.ToInt64():X}");
#endif
DrawHelper(

View file

@ -18,6 +18,7 @@ using Dalamud.Plugin;
using Dalamud.Plugin.Internal;
using Dalamud.Plugin.Internal.Exceptions;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Utility;
using ImGuiNET;
using ImGuiScene;
@ -613,6 +614,8 @@ namespace Dalamud.Interface.Internal.Windows
label += Locs.PluginTitleMod_TestingVersion;
}
ImGui.PushID($"available{index}{manifest.InternalName}");
if (this.DrawPluginCollapsingHeader(label, manifest, false, false, index))
{
ImGuiHelpers.ScaledDummy(5);
@ -708,6 +711,8 @@ namespace Dalamud.Interface.Internal.Windows
ImGui.EndPopup();
}
ImGui.PopID();
}
private void DrawInstalledPlugin(LocalPlugin plugin, int index, bool showInstalled = false)
@ -781,6 +786,8 @@ namespace Dalamud.Interface.Internal.Windows
trouble = true;
}
ImGui.PushID($"installed{index}{plugin.Manifest.InternalName}");
if (this.DrawPluginCollapsingHeader(label, plugin.Manifest, trouble, availablePluginUpdate != default, index))
{
var manifest = plugin.Manifest;
@ -791,7 +798,9 @@ namespace Dalamud.Interface.Internal.Windows
ImGui.Text(manifest.Name);
// Download count
var downloadText = manifest.DownloadCount > 0
var downloadText = plugin.IsDev
? Locs.PluginBody_AuthorWithoutDownloadCount(manifest.Author)
: manifest.DownloadCount > 0
? Locs.PluginBody_AuthorWithDownloadCount(manifest.Author, manifest.DownloadCount)
: Locs.PluginBody_AuthorWithDownloadCountUnavailable(manifest.Author);
@ -799,12 +808,15 @@ namespace Dalamud.Interface.Internal.Windows
ImGui.TextColored(ImGuiColors.DalamudGrey3, downloadText);
// Installed from
if (!string.IsNullOrEmpty(manifest.InstalledFromUrl))
if (plugin.IsDev)
{
var fileText = Locs.PluginBody_DevPluginPath(plugin.DllFile.FullName);
ImGui.TextColored(ImGuiColors.DalamudGrey3, fileText);
}
else if (!string.IsNullOrEmpty(manifest.InstalledFromUrl))
{
var repoText = Locs.PluginBody_Plugin3rdPartyRepo(manifest.InstalledFromUrl);
ImGui.TextColored(ImGuiColors.DalamudGrey3, repoText);
ImGuiHelpers.ScaledDummy(2);
}
// Description
@ -879,6 +891,8 @@ namespace Dalamud.Interface.Internal.Windows
ImGui.EndPopup();
}
ImGui.PopID();
}
private void DrawPluginControlButton(LocalPlugin plugin)
@ -1493,10 +1507,14 @@ namespace Dalamud.Interface.Internal.Windows
#region Plugin body
public static string PluginBody_AuthorWithoutDownloadCount(string author) => Loc.Localize("InstallerAuthorWithoutDownloadCount", " by {0}").Format(author);
public static string PluginBody_AuthorWithDownloadCount(string author, long count) => Loc.Localize("InstallerAuthorWithDownloadCount", " by {0}, {1} downloads").Format(author, count);
public static string PluginBody_AuthorWithDownloadCountUnavailable(string author) => Loc.Localize("InstallerAuthorWithDownloadCountUnavailable", " by {0}, download count unavailable").Format(author);
public static string PluginBody_DevPluginPath(string path) => Loc.Localize("InstallerDevPluginPath", "From {0}").Format(path);
public static string PluginBody_Plugin3rdPartyRepo(string url) => Loc.Localize("InstallerPlugin3rdPartyRepo", "From custom plugin repository {0}").Format(url);
public static string PluginBody_AvailableDevPlugin => Loc.Localize("InstallerDevPlugin", " This plugin is available in one of your repos, please remove it from the devPlugins folder.");

View file

@ -2,6 +2,7 @@ using System;
using System.Linq;
using System.Reflection;
using Dalamud.Game;
using Dalamud.Game.Internal;
using Dalamud.Hooking.Internal;
using Dalamud.Interface.Windowing;

View file

@ -44,16 +44,21 @@ namespace Dalamud.Interface.Internal.Windows
private bool doDocking;
private bool doViewport;
private bool doGamepad;
private List<ThirdPartyRepoSettings> thirdRepoList;
private bool thirdRepoListChanged;
private string thirdRepoTempUrl = string.Empty;
private string thirdRepoAddError = string.Empty;
private List<DevPluginLocationSettings> devPluginLocations;
private bool devPluginLocationsChanged;
private string devPluginTempLocation = string.Empty;
private string devPluginLocationAddError = string.Empty;
private bool printPluginsWelcomeMsg;
private bool autoUpdatePlugins;
private bool doButtonsSystemMenu;
private string thirdRepoTempUrl = string.Empty;
private string thirdRepoAddError = string.Empty;
#region Experimental
private bool doPluginTest;
@ -88,6 +93,7 @@ namespace Dalamud.Interface.Internal.Windows
this.doPluginTest = this.dalamud.Configuration.DoPluginTest;
this.thirdRepoList = this.dalamud.Configuration.ThirdRepoList.Select(x => x.Clone()).ToList();
this.devPluginLocations = this.dalamud.Configuration.DevPluginLoadLocations.Select(x => x.Clone()).ToList();
this.printPluginsWelcomeMsg = this.dalamud.Configuration.PrintPluginsWelcomeMsg;
this.autoUpdatePlugins = this.dalamud.Configuration.AutoUpdatePlugins;
@ -144,14 +150,15 @@ namespace Dalamud.Interface.Internal.Windows
public override void OnOpen()
{
this.thirdRepoListChanged = false;
this.devPluginLocationsChanged = false;
}
/// <inheritdoc/>
public override void OnClose()
{
ImGui.GetIO().FontGlobalScale = this.dalamud.Configuration.GlobalUiScale;
this.thirdRepoList = this.dalamud.Configuration.ThirdRepoList.Select(x => x.Clone()).ToList();
this.devPluginLocations = this.dalamud.Configuration.DevPluginLoadLocations.Select(x => x.Clone()).ToList();
}
/// <inheritdoc/>
@ -252,12 +259,18 @@ namespace Dalamud.Interface.Internal.Windows
if (ImGui.BeginTabItem(Loc.Localize("DalamudSettingsExperimental", "Experimental")))
{
#region Plugin testing
ImGui.Checkbox(Loc.Localize("DalamudSettingsPluginTest", "Get plugin testing builds"), ref this.doPluginTest);
ImGui.TextColored(this.hintTextColor, Loc.Localize("DalamudSettingsPluginTestHint", "Receive testing prereleases for plugins."));
ImGui.TextColored(this.warnTextColor, Loc.Localize("DalamudSettingsPluginTestWarning", "Testing plugins may not have been vetted before being published. Please only enable this if you are aware of the risks."));
#endregion
ImGuiHelpers.ScaledDummy(12);
#region Hidden plugins
if (ImGui.Button(Loc.Localize("DalamudSettingsClearHidden", "Clear hidden plugins")))
{
this.dalamud.Configuration.HiddenPluginInternalName.Clear();
@ -266,10 +279,12 @@ namespace Dalamud.Interface.Internal.Windows
ImGui.TextColored(this.hintTextColor, Loc.Localize("DalamudSettingsClearHiddenHint", "Restore plugins you have previously hidden from the plugin installer."));
ImGuiHelpers.ScaledDummy(12);
#endregion
ImGuiHelpers.ScaledDummy(12);
#region Custom repos
ImGui.Text(Loc.Localize("DalamudSettingsCustomRepo", "Custom Plugin Repositories"));
ImGui.TextColored(this.hintTextColor, Loc.Localize("DalamudSettingCustomRepoHint", "Add custom plugin repositories."));
ImGui.TextColored(this.warnTextColor, Loc.Localize("DalamudSettingCustomRepoWarning", "We cannot take any responsibility for third-party plugins and repositories.\nTake care when installing third-party plugins from untrusted sources."));
@ -278,7 +293,7 @@ namespace Dalamud.Interface.Internal.Windows
ImGui.Columns(4);
ImGui.SetColumnWidth(0, 18 + (5 * ImGuiHelpers.GlobalScale));
ImGui.SetColumnWidth(1, ImGui.GetWindowWidth() - (18 + 16 + 14) - ((5 + 45 + 26) * ImGuiHelpers.GlobalScale));
ImGui.SetColumnWidth(1, ImGui.GetWindowContentRegionWidth() - (18 + 16 + 14) - ((5 + 45 + 26) * ImGuiHelpers.GlobalScale));
ImGui.SetColumnWidth(2, 16 + (45 * ImGuiHelpers.GlobalScale));
ImGui.SetColumnWidth(3, 14 + (26 * ImGuiHelpers.GlobalScale));
@ -303,7 +318,7 @@ namespace Dalamud.Interface.Internal.Windows
ImGui.NextColumn();
ImGui.Separator();
ThirdPartyRepoSettings toRemove = null;
ThirdPartyRepoSettings repoToRemove = null;
var repoNumber = 1;
foreach (var thirdRepoSetting in this.thirdRepoList)
@ -325,7 +340,7 @@ namespace Dalamud.Interface.Internal.Windows
if (ImGuiComponents.IconButton(FontAwesomeIcon.Trash))
{
toRemove = thirdRepoSetting;
repoToRemove = thirdRepoSetting;
}
ImGui.NextColumn();
@ -336,9 +351,9 @@ namespace Dalamud.Interface.Internal.Windows
repoNumber++;
}
if (toRemove != null)
if (repoToRemove != null)
{
this.thirdRepoList.Remove(toRemove);
this.thirdRepoList.Remove(repoToRemove);
this.thirdRepoListChanged = true;
}
@ -377,6 +392,115 @@ namespace Dalamud.Interface.Internal.Windows
{
ImGui.TextColored(new Vector4(1, 0, 0, 1), this.thirdRepoAddError);
}
#endregion
ImGuiHelpers.ScaledDummy(12);
#region Custom dev plugin load locations
ImGui.Text(Loc.Localize("DalamudSettingsDevPluginLocation", "Dev Plugin Locations"));
ImGui.TextColored(this.hintTextColor, Loc.Localize("DalamudSettingsDevPluginLocationsHint", "Add additional dev plugin load locations.\nThese can be either the directory or DLL path."));
ImGuiHelpers.ScaledDummy(5);
ImGui.Columns(4);
ImGui.SetColumnWidth(0, 18 + (5 * ImGuiHelpers.GlobalScale));
ImGui.SetColumnWidth(1, ImGui.GetWindowContentRegionWidth() - (18 + 16 + 14) - ((5 + 45 + 26) * ImGuiHelpers.GlobalScale));
ImGui.SetColumnWidth(2, 16 + (45 * ImGuiHelpers.GlobalScale));
ImGui.SetColumnWidth(3, 14 + (26 * ImGuiHelpers.GlobalScale));
ImGui.Separator();
ImGui.Text("#");
ImGui.NextColumn();
ImGui.Text("Path");
ImGui.NextColumn();
ImGui.Text("Enabled");
ImGui.NextColumn();
ImGui.Text(string.Empty);
ImGui.NextColumn();
ImGui.Separator();
DevPluginLocationSettings locationToRemove = null;
var locNumber = 1;
foreach (var devPluginLocationSetting in this.devPluginLocations)
{
var isEnabled = devPluginLocationSetting.IsEnabled;
ImGui.PushID($"devPluginLocation_{devPluginLocationSetting.Path}");
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + (ImGui.GetColumnWidth() / 2) - 8 - (ImGui.CalcTextSize(repoNumber.ToString()).X / 2));
ImGui.Text(locNumber.ToString());
ImGui.NextColumn();
ImGui.TextWrapped(devPluginLocationSetting.Path);
ImGui.NextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + (ImGui.GetColumnWidth() / 2) - 7 - (12 * ImGuiHelpers.GlobalScale));
ImGui.Checkbox("##devPluginLocationCheck", ref isEnabled);
ImGui.NextColumn();
if (ImGuiComponents.IconButton(FontAwesomeIcon.Trash))
{
locationToRemove = devPluginLocationSetting;
}
ImGui.NextColumn();
ImGui.Separator();
devPluginLocationSetting.IsEnabled = isEnabled;
locNumber++;
}
if (locationToRemove != null)
{
this.devPluginLocations.Remove(locationToRemove);
this.devPluginLocationsChanged = true;
}
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + (ImGui.GetColumnWidth() / 2) - 8 - (ImGui.CalcTextSize(locNumber.ToString()).X / 2));
ImGui.Text(locNumber.ToString());
ImGui.NextColumn();
ImGui.SetNextItemWidth(-1);
ImGui.InputText("##devPluginLocationInput", ref this.devPluginTempLocation, 300);
ImGui.NextColumn();
// Enabled button
ImGui.NextColumn();
if (!string.IsNullOrEmpty(this.devPluginTempLocation) && ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
{
if (this.devPluginLocations.Any(r => string.Equals(r.Path, this.devPluginTempLocation, StringComparison.InvariantCultureIgnoreCase)))
{
this.devPluginLocationAddError = Loc.Localize("DalamudDevPluginLocationExists", "Location already exists.");
Task.Delay(5000).ContinueWith(t => this.devPluginLocationAddError = string.Empty);
}
else
{
this.devPluginLocations.Add(new DevPluginLocationSettings
{
Path = this.devPluginTempLocation,
IsEnabled = true,
});
this.devPluginLocationsChanged = true;
this.devPluginTempLocation = string.Empty;
}
}
ImGui.Columns(1);
ImGui.EndTabItem();
if (!string.IsNullOrEmpty(this.devPluginLocationAddError))
{
ImGui.TextColored(new Vector4(1, 0, 0, 1), this.devPluginLocationAddError);
}
#endregion
ImGuiHelpers.ScaledDummy(12);
}
ImGui.EndTabBar();
@ -384,20 +508,18 @@ namespace Dalamud.Interface.Internal.Windows
ImGui.EndChild();
if (ImGui.Button(Loc.Localize("Save", "Save")))
{
this.Save();
var buttonSave = false;
var buttonClose = false;
if (this.thirdRepoListChanged)
{
this.dalamud.PluginManager.SetPluginReposFromConfig(true);
this.thirdRepoListChanged = false;
}
}
if (ImGui.Button(Loc.Localize("Save", "Save")))
buttonSave = true;
ImGui.SameLine();
if (ImGui.Button(Loc.Localize("SaveAndClose", "Save and Close")))
buttonSave = buttonClose = true;
if (buttonSave)
{
this.Save();
@ -407,6 +529,15 @@ namespace Dalamud.Interface.Internal.Windows
this.thirdRepoListChanged = false;
}
if (this.devPluginLocationsChanged)
{
this.dalamud.PluginManager.ScanDevPlugins();
this.devPluginLocationsChanged = false;
}
}
if (buttonClose)
{
this.IsOpen = false;
}
}
@ -456,6 +587,7 @@ namespace Dalamud.Interface.Internal.Windows
this.dalamud.Configuration.DoPluginTest = this.doPluginTest;
this.dalamud.Configuration.ThirdRepoList = this.thirdRepoList.Select(x => x.Clone()).ToList();
this.dalamud.Configuration.DevPluginLoadLocations = this.devPluginLocations.Select(x => x.Clone()).ToList();
this.dalamud.Configuration.PrintPluginsWelcomeMsg = this.printPluginsWelcomeMsg;
this.dalamud.Configuration.AutoUpdatePlugins = this.autoUpdatePlugins;

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Interface.Internal;
using ImGuiNET;
using ImGuiScene;

View file

@ -8,6 +8,131 @@ namespace Dalamud.Logging
/// </summary>
public static class PluginLog
{
#region "Log" prefixed Serilog style methods
/// <summary>
/// Log a templated message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Log(string messageTemplate, params object[] values)
=> Serilog.Log.Information($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Log(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Information(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated verbose message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogVerbose(string messageTemplate, params object[] values)
=> Serilog.Log.Verbose($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated verbose message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogVerbose(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Verbose(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated debug message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogDebug(string messageTemplate, params object[] values)
=> Serilog.Log.Debug($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated debug message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogDebug(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Debug(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated information message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogInformation(string messageTemplate, params object[] values)
=> Serilog.Log.Information($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated information message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogInformation(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Information(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated warning message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogWarning(string messageTemplate, params object[] values)
=> Serilog.Log.Warning($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated warning message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogWarning(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Warning(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated error message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogError(string messageTemplate, params object[] values)
=> Serilog.Log.Error($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated error message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogError(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Error(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated fatal message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogFatal(string messageTemplate, params object[] values)
=> Serilog.Log.Fatal($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated fatal message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogFatal(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Fatal(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
#endregion
#region Serilog style methods
/// <summary>
/// Log a templated verbose message to the in-game debug log.
/// </summary>
@ -109,5 +234,7 @@ namespace Dalamud.Logging
/// <param name="values">Values to log.</param>
public static void Fatal(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Fatal(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
#endregion
}
}

Some files were not shown because too many files have changed in this diff Show more