From 1894d579a8ec77590d839607f326983967a99d79 Mon Sep 17 00:00:00 2001
From: meli <57847713+ff-meli@users.noreply.github.com>
Date: Tue, 31 Mar 2020 09:34:47 -0700
Subject: [PATCH] initial map link payload implementation. Generally works,
but at least needs a better way to handle lumina DI
---
Dalamud/Dalamud.cs | 3 +
Dalamud/Game/Chat/SeStringHandling/Payload.cs | 69 +++++++--
.../Game/Chat/SeStringHandling/PayloadType.cs | 4 +
.../SeStringHandling/Payloads/ItemPayload.cs | 2 +-
.../Payloads/MapLinkPayload.cs | 143 ++++++++++++++++++
.../Game/Chat/SeStringHandling/SeString.cs | 5 +-
6 files changed, 210 insertions(+), 16 deletions(-)
create mode 100644 Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs
diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs
index 3ed3b2ef8..5dcc85c8e 100644
--- a/Dalamud/Dalamud.cs
+++ b/Dalamud/Dalamud.cs
@@ -88,6 +88,9 @@ namespace Dalamud {
this.Data = new DataManager(this.StartInfo.Language);
this.Data.Initialize();
+ // FIXME: need a better way to get this into the string payloads
+ Game.Chat.SeStringHandling.SeString.DataResolver = this.Data;
+
this.ClientState = new ClientState(this, info, this.SigScanner);
this.BotManager = new DiscordBotManager(this, this.Configuration.DiscordFeatureConfig);
diff --git a/Dalamud/Game/Chat/SeStringHandling/Payload.cs b/Dalamud/Game/Chat/SeStringHandling/Payload.cs
index fbbaf2ab8..d0c17ecf8 100644
--- a/Dalamud/Game/Chat/SeStringHandling/Payload.cs
+++ b/Dalamud/Game/Chat/SeStringHandling/Payload.cs
@@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using Dalamud.Data;
using Dalamud.Game.Chat.SeStringHandling.Payloads;
using Serilog;
@@ -16,22 +17,32 @@ namespace Dalamud.Game.Chat.SeStringHandling
{
public abstract PayloadType Type { get; }
+ protected DataManager dataResolver;
+
public abstract void Resolve();
public abstract byte[] Encode();
protected abstract void ProcessChunkImpl(BinaryReader reader, long endOfStream);
- public static Payload Process(BinaryReader reader)
+ public static Payload Process(BinaryReader reader, DataManager dataResolver)
{
+ Payload payload = null;
if ((byte)reader.PeekChar() != START_BYTE)
{
- return ProcessText(reader);
+ payload = ProcessText(reader);
}
else
{
- return ProcessChunk(reader);
+ payload = ProcessChunk(reader);
}
+
+ if (payload != null)
+ {
+ payload.dataResolver = dataResolver;
+ }
+
+ return payload;
}
private static Payload ProcessChunk(BinaryReader reader)
@@ -59,6 +70,10 @@ namespace Dalamud.Game.Chat.SeStringHandling
payload = new ItemPayload();
break;
+ case EmbeddedInfoType.MapPositionLink:
+ payload = new MapLinkPayload();
+ break;
+
case EmbeddedInfoType.Status:
payload = new StatusPayload();
break;
@@ -74,6 +89,7 @@ namespace Dalamud.Game.Chat.SeStringHandling
}
}
break;
+
default:
Log.Verbose("Unhandled SeStringChunkType: {0}", chunkType);
payload = new RawPayload((byte)chunkType);
@@ -111,6 +127,7 @@ namespace Dalamud.Game.Chat.SeStringHandling
{
PlayerName = 0x01,
ItemLink = 0x03,
+ MapPositionLink = 0x04,
Status = 0x09,
LinkTerminator = 0xCF // not clear but seems to always follow a link
@@ -118,10 +135,14 @@ namespace Dalamud.Game.Chat.SeStringHandling
protected enum IntegerType
{
+ // used as an internal marker; sometimes single bytes are bare with no marker at all
+ None = 0,
+
Byte = 0xF0,
ByteTimes256 = 0xF1,
Int16 = 0xF2,
- Int16Plus1Million = 0xF6,
+ Int16Packed = 0xF4, // seen in map links, seemingly 2 8-bit values packed into 2 bytes with only one marker
+ Int24Special = 0xF6, // unsure how different form Int24 - used for hq items that add 1 million, also used for normal 24-bit values in map links
Int24 = 0xFA,
Int32 = 0xFE
}
@@ -147,25 +168,22 @@ namespace Dalamud.Game.Chat.SeStringHandling
{
case IntegerType.Byte:
return input.ReadByte();
+
case IntegerType.ByteTimes256:
return input.ReadByte() * 256;
+
case IntegerType.Int16:
+ // fallthrough - same logic
+ case IntegerType.Int16Packed:
{
var v = 0;
v |= input.ReadByte() << 8;
v |= input.ReadByte();
return v;
}
- case IntegerType.Int16Plus1Million:
- {
- var v = 0;
- v |= input.ReadByte() << 16;
- v |= input.ReadByte() << 8;
- v |= input.ReadByte();
- // need the actual value since it's used as a flag
- // v -= 1000000;
- return v;
- }
+
+ case IntegerType.Int24Special:
+ // Fallthrough - same logic
case IntegerType.Int24:
{
var v = 0;
@@ -174,6 +192,7 @@ namespace Dalamud.Game.Chat.SeStringHandling
v |= input.ReadByte();
return v;
}
+
case IntegerType.Int32:
{
var v = 0;
@@ -183,6 +202,7 @@ namespace Dalamud.Game.Chat.SeStringHandling
v |= input.ReadByte();
return v;
}
+
default:
throw new NotSupportedException();
}
@@ -220,6 +240,27 @@ namespace Dalamud.Game.Chat.SeStringHandling
throw new NotSupportedException();
}
+
+ protected static (int, int) GetPackedIntegers(BinaryReader input)
+ {
+ var value = (uint)GetInteger(input);
+ if (value > 0xFFFF)
+ {
+ return ((int)((value & 0xFFFF0000) >> 16), (int)(value & 0xFFFF));
+ }
+ else if (value > 0xFF)
+ {
+ return ((int)((value & 0xFF00) >> 8), (int)(value & 0xFF));
+ }
+
+ // unsure if there are other cases, like "odd" pairings of 2+1 bytes etc
+ throw new NotSupportedException();
+ }
+
+ protected static byte[] MakePackedInteger(int val1, int val2)
+ {
+ return MakeInteger(val1).Concat(MakeInteger(val2)).ToArray();
+ }
#endregion
}
}
diff --git a/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs b/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs
index 6576e0524..77f99c54c 100644
--- a/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs
+++ b/Dalamud/Game/Chat/SeStringHandling/PayloadType.cs
@@ -28,6 +28,10 @@ namespace Dalamud.Game.Chat.SeStringHandling
///
RawText,
///
+ /// An SeString payload representing a map position link, such as from <flag> or <pos>.
+ ///
+ MapLink,
+ ///
/// An SeString payload representing any data we don't handle.
///
Unknown
diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs
index 1a1dc0bb7..23b77c238 100644
--- a/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs
+++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/ItemPayload.cs
@@ -38,7 +38,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
var idBytes = MakeInteger(actualItemId);
bool hasName = !string.IsNullOrEmpty(ItemName);
- var itemIdFlag = IsHQ ? IntegerType.Int16Plus1Million : IntegerType.Int16;
+ var itemIdFlag = IsHQ ? IntegerType.Int24Special : IntegerType.Int16;
var chunkLen = idBytes.Length + 5;
if (hasName)
diff --git a/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs b/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs
new file mode 100644
index 000000000..512a60a9a
--- /dev/null
+++ b/Dalamud/Game/Chat/SeStringHandling/Payloads/MapLinkPayload.cs
@@ -0,0 +1,143 @@
+using Lumina.Excel.GeneratedSheets;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Dalamud.Game.Chat.SeStringHandling.Payloads
+{
+ public class MapLinkPayload : Payload
+ {
+ public override PayloadType Type => PayloadType.MapLink;
+
+ // pre-Resolve() values
+ public int TerritoryTypeId { get; set; }
+ public int MapId { get; set; }
+ public uint RawX { get; set; }
+ public uint RawY { get; set; }
+
+ // Resolved values
+ // It might make sense to have Territory be an external type, that has assorted relevant info
+ public string Territory { get; private set; }
+ public string Zone { get; private set; }
+ public float XCoord { get; private set; }
+ public float YCoord { get; private set; }
+ // Z?
+
+ public override byte[] Encode()
+ {
+ // TODO: for now we just encode the raw/internal values
+ // eventually we should allow creation using 'nice' values that then encode properly
+
+ var packedTerritoryAndMapBytes = WrapPackedIntegerAndMarker(TerritoryTypeId, MapId);
+ var xBytes = WrapIntegerAndMarker((int)RawX);
+ var yBytes = WrapIntegerAndMarker((int)RawY);
+
+ var chunkLen = 4 + packedTerritoryAndMapBytes.Length + xBytes.Length + yBytes.Length;
+
+ var bytes = new List()
+ {
+ START_BYTE,
+ (byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.MapPositionLink
+ };
+
+ bytes.AddRange(packedTerritoryAndMapBytes);
+ bytes.AddRange(xBytes);
+ bytes.AddRange(yBytes);
+
+ // unk
+ bytes.AddRange(new byte[] { 0xFF, 0x01, END_BYTE });
+
+ return bytes.ToArray();
+ }
+
+ public override void Resolve()
+ {
+ if (string.IsNullOrEmpty(Territory))
+ {
+ var terrRow = dataResolver.GetExcelSheet().GetRow(TerritoryTypeId);
+ Territory = dataResolver.GetExcelSheet().GetRow(terrRow.PlaceName).Name;
+ Zone = dataResolver.GetExcelSheet().GetRow(terrRow.PlaceNameZone).Name;
+
+ var mapSizeFactor = dataResolver.GetExcelSheet