diff --git a/WaymarkStudio/Extensions.cs b/WaymarkStudio/Extensions.cs index 513f486..baf4a89 100644 --- a/WaymarkStudio/Extensions.cs +++ b/WaymarkStudio/Extensions.cs @@ -5,13 +5,14 @@ using Lumina.Excel.Sheets; using System; using System.Collections.Generic; +using System.IO; using System.Numerics; using System.Runtime.CompilerServices; namespace WaymarkStudio; internal static class Extensions { - static bool IsBitSet(byte b, int pos) + internal static bool IsBitSet(byte b, int pos) { return (b & (1 << pos)) != 0; } @@ -115,4 +116,15 @@ internal static Vector3 ComputeNormal(this RaycastHit hit) Vector3 edge2 = hit.V3 - hit.V1; return Vector3.Normalize(Vector3.Cross(edge1, edge2)); } + + public static void Write7BitEncodedIntSigned(this BinaryWriter writer, int value) + { + writer.Write7BitEncodedInt((value << 1) ^ (value >> 31)); + } + + public static int Read7BitEncodedIntSigned(this BinaryReader reader) + { + int value = reader.Read7BitEncodedInt(); + return (value >>> 1) ^ -(value & 1); + } } diff --git a/WaymarkStudio/WaymarkPreset.cs b/WaymarkStudio/WaymarkPreset.cs index f6af003..ecb5154 100644 --- a/WaymarkStudio/WaymarkPreset.cs +++ b/WaymarkStudio/WaymarkPreset.cs @@ -3,6 +3,7 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; +using System.IO; using System.Numerics; namespace WaymarkStudio; @@ -51,4 +52,66 @@ internal static ushort TerritoryIdForContendId(ushort contentId) var row = contentSheet.GetRow(contentId); return (ushort)row.TerritoryType.Value.RowId; } + + internal unsafe byte[] Serialize() + { + using (var memoryStream = new MemoryStream()) + { + using (var writer = new BinaryWriter(memoryStream)) + { + writer.Write(TerritoryId); + writer.Write(ContentFinderConditionId); + // write empty bitmask to advance the offset + byte active = 0; + writer.Write(active); + foreach (Waymark w in Enum.GetValues<Waymark>()) + { + if (MarkerPositions.ContainsKey(w)) + { + var index = (int)w; + active |= (byte)(1 << index); + + var position = MarkerPositions[w].ToGamePresetPoint(); + writer.Write7BitEncodedIntSigned(position.X); + writer.Write7BitEncodedIntSigned(position.Y); + writer.Write7BitEncodedIntSigned(position.Z); + } + } + writer.Write(Name); + // write filled bitmask + writer.Seek(4, SeekOrigin.Begin); + writer.Write(active); + } + return memoryStream.ToArray(); + } + } + + internal static WaymarkPreset Deserialize(byte[] b) + { + WaymarkPreset preset = new(); + using (var memoryStream = new MemoryStream(b)) + { + using (var reader = new BinaryReader(memoryStream)) + { + preset.TerritoryId = reader.ReadUInt16(); + preset.ContentFinderConditionId = reader.ReadUInt16(); + byte active = reader.ReadByte(); + foreach (Waymark w in Enum.GetValues<Waymark>()) + { + if (Extensions.IsBitSet(active, (int)w)) + { + GamePresetPoint position = new() + { + X = reader.Read7BitEncodedIntSigned(), + Y = reader.Read7BitEncodedIntSigned(), + Z = reader.Read7BitEncodedIntSigned() + }; + preset.MarkerPositions.Add(w, position.ToWorldPosition()); + } + } + preset.Name = reader.ReadString(); + } + } + return preset; + } } diff --git a/WaymarkStudio/Windows/StudioWindow.cs b/WaymarkStudio/Windows/StudioWindow.cs index 985844b..95d9119 100644 --- a/WaymarkStudio/Windows/StudioWindow.cs +++ b/WaymarkStudio/Windows/StudioWindow.cs @@ -12,6 +12,7 @@ namespace WaymarkStudio.Windows; internal class StudioWindow : Window, IDisposable { + internal const string presetb64Prefix = "wms0"; private readonly Vector2 iconButtonSize = new(30, 30); bool isHoverPreview = false; bool wasHoverPreview = false; @@ -53,7 +54,6 @@ public unsafe override void Draw() } ImGui.SameLine(); DrawSavedPresets(); - if (wasHoverPreview && !isHoverPreview) Plugin.WaymarkManager.ClearHoverPreview(); wasHoverPreview = isHoverPreview; @@ -251,6 +251,19 @@ internal void DrawSavedPresets() HoverTooltip("Clear Waymarks"); } ImGui.Text("Saved Presets"); + ImGui.SameLine(); + string clipboard = ImGui.GetClipboardText(); + if (clipboard.StartsWith(presetb64Prefix)) + { + if (ImGuiComponents.IconButton($"import_preset", FontAwesomeIcon.FileImport)) + { + var preset = WaymarkPreset.Deserialize(Convert.FromBase64String(clipboard.Substring(presetb64Prefix.Length))); + Plugin.Config.SavedPresets.Add(preset); + Plugin.Config.Save(); + ImGui.SetClipboardText(""); + } + HoverTooltip("Import From clipboard"); + } var presets = Plugin.Config.SavedPresets; deleteIndex = -1; @@ -337,6 +350,12 @@ internal void DrawPresetRow(int i, WaymarkPreset preset, bool isReadOnly = false deleteIndex = i; } HoverTooltip("Delete preset"); + ImGui.SameLine(); + if (ImGuiComponents.IconButton($"export_preset##{i}", FontAwesomeIcon.FileExport)) + { + ImGui.SetClipboardText(presetb64Prefix + Convert.ToBase64String(preset.Serialize())); + } + HoverTooltip("Export to clipboard"); } }