Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement SeString renderer #1977

Merged
merged 1 commit into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading