Skip to content

Commit

Permalink
Implement SeString renderer (#1977)
Browse files Browse the repository at this point in the history
Does not have contextual evaluation. Only styling payloads are handled for (relative) simplicity.
  • Loading branch information
Soreepeong authored Jul 28, 2024
1 parent 1c0ad61 commit 844b04f
Show file tree
Hide file tree
Showing 24 changed files with 14,665 additions and 21 deletions.
7 changes: 7 additions & 0 deletions Dalamud/Dalamud.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@
</Content>
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Interface\Internal\ImGuiSeStringRenderer\TextProcessing\LineBreak.txt" LogicalName="LineBreak.txt" />
<EmbeddedResource Include="Interface\Internal\ImGuiSeStringRenderer\TextProcessing\EastAsianWidth.txt" LogicalName="EastAsianWidth.txt" />
<EmbeddedResource Include="Interface\Internal\ImGuiSeStringRenderer\TextProcessing\DerivedGeneralCategory.txt" LogicalName="DerivedGeneralCategory.txt" />
<EmbeddedResource Include="Interface\Internal\ImGuiSeStringRenderer\TextProcessing\emoji-data.txt" LogicalName="emoji-data.txt" />
</ItemGroup>

<Target Name="AddRuntimeDependenciesToContent" BeforeTargets="GetCopyToOutputDirectoryItems" DependsOnTargets="GenerateBuildDependencyFile;GenerateBuildRuntimeConfigurationFiles">
<ItemGroup>
<ContentWithTargetPath Include="$(ProjectDepsFilePath)" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectDepsFileName)" />
Expand Down
11 changes: 9 additions & 2 deletions Dalamud/Game/Config/GameConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,10 @@ private unsafe GameConfig(Framework framework, TargetSigScanner sigScanner)

/// <inheritdoc/>
public bool TryGet(SystemConfigOption option, out StringConfigProperties? properties) => this.System.TryGetProperties(option.GetName(), out properties);


/// <inheritdoc/>
public bool TryGet(SystemConfigOption option, out PadButtonValue value) => this.System.TryGetStringAsEnum(option.GetName(), out value);

/// <inheritdoc/>
public bool TryGet(UiConfigOption option, out bool value) => this.UiConfig.TryGet(option.GetName(), out value);

Expand Down Expand Up @@ -346,7 +349,11 @@ public bool TryGet(SystemConfigOption option, out FloatConfigProperties? propert
/// <inheritdoc/>
public bool TryGet(SystemConfigOption option, out StringConfigProperties? properties)
=> this.gameConfigService.TryGet(option, out properties);


/// <inheritdoc/>
public bool TryGet(SystemConfigOption option, out PadButtonValue value)
=> this.gameConfigService.TryGet(option, out value);

/// <inheritdoc/>
public bool TryGet(UiConfigOption option, out bool value)
=> this.gameConfigService.TryGet(option, out value);
Expand Down
35 changes: 35 additions & 0 deletions Dalamud/Game/Config/GameConfigSection.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Text;

using Dalamud.Memory;
using Dalamud.Utility;
Expand Down Expand Up @@ -357,6 +358,40 @@ public string GetString(string name)
return value;
}

/// <summary>Attempts to get a string config value as an enum value.</summary>
/// <param name="name">Name of the config option.</param>
/// <param name="value">The returned value of the config option.</param>
/// <typeparam name="T">Type of the enum. Name of each enum fields are compared against.</typeparam>
/// <returns>A value representing the success.</returns>
public unsafe bool TryGetStringAsEnum<T>(string name, out T value) where T : struct, Enum
{
value = default;
if (!this.TryGetIndex(name, out var index))
{
return false;
}

if (!this.TryGetEntry(index, out var entry))
{
return false;
}

if (entry->Type != 4)
{
return false;
}

if (entry->Value.String == null)
{
return false;
}

var n8 = entry->Value.String->AsSpan();
Span<char> n16 = stackalloc char[Encoding.UTF8.GetCharCount(n8)];
Encoding.UTF8.GetChars(n8, n16);
return Enum.TryParse(n16, out value);
}

/// <summary>
/// Set a string config option.
/// Note: Not all config options will be be immediately reflected in the game.
Expand Down
85 changes: 85 additions & 0 deletions Dalamud/Game/Config/PadButtonValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
namespace Dalamud.Game.Config;

// ReSharper disable InconsistentNaming
// ReSharper disable IdentifierTypo
// ReSharper disable CommentTypo

/// <summary>Valid values for PadButton options under <see cref="SystemConfigOption"/>.</summary>
/// <remarks>Names are the valid part. Enum values are exclusively for use with current Dalamud version.</remarks>
public enum PadButtonValue
{
/// <summary>Auto-run.</summary>
Autorun_Support,

/// <summary>Change Hotbar Set.</summary>
Hotbar_Set_Change,

/// <summary>Highlight Left Hotbar.</summary>
XHB_Left_Start,

/// <summary>Highlight Right Hotbar.</summary>
XHB_Right_Start,

/// <summary>Not directly referenced by Gamepad button customization window.</summary>
Cursor_Operation,

/// <summary>Draw Weapon/Lock On.</summary>
Lockon_and_Sword,

/// <summary>Sit/Lock On.</summary>
Lockon_and_Sit,

/// <summary>Change Camera.</summary>
Camera_Modechange,

/// <summary>Reset Camera Position.</summary>
Camera_Reset,

/// <summary>Draw/Sheathe Weapon.</summary>
Drawn_Sword,

/// <summary>Lock On.</summary>
Camera_Lockononly,

/// <summary>Face Target.</summary>
FaceTarget,

/// <summary>Assist Target.</summary>
AssistTarget,

/// <summary>Face Camera.</summary>
LookCamera,

/// <summary>Execute Macro #98 (Exclusive).</summary>
Macro98,

/// <summary>Execute Macro #99 (Exclusive).</summary>
Macro99,

/// <summary>Not Assigned.</summary>
Notset,

/// <summary>Jump/Cancel Casting.</summary>
Jump,

/// <summary>Select Target/Confirm.</summary>
Accept,

/// <summary>Cancel.</summary>
Cancel,

/// <summary>Open Map/Subcommands.</summary>
Map_Sub,

/// <summary>Open Main Menu.</summary>
MainCommand,

/// <summary>Select HUD.</summary>
HUD_Select,

/// <summary>Move Character.</summary>
Move_Operation,

/// <summary>Move Camera.</summary>
Camera_Operation,
}
149 changes: 149 additions & 0 deletions Dalamud/Interface/Internal/ImGuiSeStringRenderer/GfdFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
using System.IO;
using System.Numerics;
using System.Runtime.InteropServices;

using Lumina.Data;

namespace Dalamud.Interface.Internal.ImGuiSeStringRenderer;

/// <summary>Reference member view of a .gfd file data.</summary>
internal sealed unsafe class GfdFile : FileResource
{
/// <summary>Gets or sets the file header.</summary>
public GfdHeader Header { get; set; }

/// <summary>Gets or sets the entries.</summary>
public GfdEntry[] Entries { get; set; } = [];

/// <inheritdoc/>
public override void LoadFile()
{
if (this.DataSpan.Length < sizeof(GfdHeader))
throw new InvalidDataException($"Not enough space for a {nameof(GfdHeader)}");
if (this.DataSpan.Length < sizeof(GfdHeader) + (this.Header.Count * sizeof(GfdEntry)))
throw new InvalidDataException($"Not enough space for all the {nameof(GfdEntry)}");

this.Header = MemoryMarshal.AsRef<GfdHeader>(this.DataSpan);
this.Entries = MemoryMarshal.Cast<byte, GfdEntry>(this.DataSpan[sizeof(GfdHeader)..]).ToArray();
}

/// <summary>Attempts to get an entry.</summary>
/// <param name="iconId">The icon ID.</param>
/// <param name="entry">The entry.</param>
/// <param name="followRedirect">Whether to follow redirects.</param>
/// <returns><c>true</c> if found.</returns>
public bool TryGetEntry(uint iconId, out GfdEntry entry, bool followRedirect = true)
{
if (iconId == 0)
{
entry = default;
return false;
}

var entries = this.Entries;
if (iconId <= this.Entries.Length && entries[(int)(iconId - 1)].Id == iconId)
{
if (iconId <= entries.Length)
{
entry = entries[(int)(iconId - 1)];
return !entry.IsEmpty;
}

entry = default;
return false;
}

var lo = 0;
var hi = entries.Length;
while (lo <= hi)
{
var i = lo + ((hi - lo) >> 1);
if (entries[i].Id == iconId)
{
if (followRedirect && entries[i].Redirect != 0)
{
iconId = entries[i].Redirect;
lo = 0;
hi = entries.Length;
continue;
}

entry = entries[i];
return !entry.IsEmpty;
}

if (entries[i].Id < iconId)
lo = i + 1;
else
hi = i - 1;
}

entry = default;
return false;
}

/// <summary>Header of a .gfd file.</summary>
[StructLayout(LayoutKind.Sequential)]
public struct GfdHeader
{
/// <summary>Signature: "gftd0100".</summary>
public fixed byte Signature[8];

/// <summary>Number of entries.</summary>
public int Count;

/// <summary>Unused/unknown.</summary>
public fixed byte Padding[4];
}

/// <summary>An entry of a .gfd file.</summary>
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct GfdEntry
{
/// <summary>ID of the entry.</summary>
public ushort Id;

/// <summary>The left offset of the entry.</summary>
public ushort Left;

/// <summary>The top offset of the entry.</summary>
public ushort Top;

/// <summary>The width of the entry.</summary>
public ushort Width;

/// <summary>The height of the entry.</summary>
public ushort Height;

/// <summary>Unknown/unused.</summary>
public ushort Unk0A;

/// <summary>The redirected entry, maybe.</summary>
public ushort Redirect;

/// <summary>Unknown/unused.</summary>
public ushort Unk0E;

/// <summary>Gets a value indicating whether this entry is effectively empty.</summary>
public bool IsEmpty => this.Width == 0 || this.Height == 0;

/// <summary>Gets or sets the size of this entry.</summary>
public Vector2 Size
{
get => new(this.Width, this.Height);
set => (this.Width, this.Height) = (checked((ushort)value.X), checked((ushort)value.Y));
}

/// <summary>Gets the UV0 of this entry.</summary>
public Vector2 Uv0 => new(this.Left / 512f, this.Top / 1024f);

/// <summary>Gets the UV1 of this entry.</summary>
public Vector2 Uv1 => new((this.Left + this.Width) / 512f, (this.Top + this.Height) / 1024f);

/// <summary>Gets the UV0 of the HQ version of this entry.</summary>
public Vector2 HqUv0 => new(this.Left / 256f, (this.Top + 170.5f) / 512f);

/// <summary>Gets the UV1 of the HQ version of this entry.</summary>
public Vector2 HqUv1 => new((this.Left + this.Width) / 256f, (this.Top + this.Height + 170.5f) / 512f);
}
}
Loading

0 comments on commit 844b04f

Please sign in to comment.