diff --git a/PostNamazu/Actions/Preset.cs b/PostNamazu/Actions/Preset.cs index 8ae4e5b..a78a456 100644 --- a/PostNamazu/Actions/Preset.cs +++ b/PostNamazu/Actions/Preset.cs @@ -1,20 +1,31 @@ using System; using System.Collections.Generic; -using PostNamazu.Attributes; -using PostNamazu.Models; +using System.Text.RegularExpressions; using Newtonsoft.Json; +using PostNamazu.Attributes; using PostNamazu.Common; -using System.Text.RegularExpressions; using PostNamazu.Common.Localization; +using PostNamazu.Models; #pragma warning disable CS0649 // 从未对字段赋值,字段将一直保持其默认值 namespace PostNamazu.Actions { internal class Preset : NamazuModule { - private IntPtr UIModulePtr; + public IntPtr GetUIModulePtr; + private IntPtr UIModulePtr => Memory.CallInjected64(GetUIModulePtr, PostNamazu.FrameworkPtr); private Int32 WayMarkSlotOffset; - public IntPtr MapIDPtr; + + // 模组使用的是 ContentFinderCondition 而非 Map 或 Territory 的 Id + public IntPtr ContentFinderConditionIdPtr; + + // FFXIVClientStructs/FFXIV/Client/UI/Misc/FieldMarkerModule + // [FieldOffset(0x48), FixedSizeArray] internal FixedSizeArray30 _presets + int WaymarkDataOffset = 0x48; + + // FFXIVClientStructs/FFXIV/Client/UI/Misc/FieldMarkerModule + // [StructLayout(LayoutKind.Explicit, Size = 0x68)] public partial struct FieldMarkerPreset + int FieldMarkerPresetSize = 0x68; // 本地化字符串定义 [LocalizationProvider("Preset")] @@ -27,13 +38,20 @@ private static class Localizations public override void GetOffsets() { base.GetOffsets(); - var GetUiModulePtr = SigScanner.ScanText("E8 * * * * 80 7B 1D 01", "GetUiModulePtr"); - UIModulePtr = Memory.CallInjected64(GetUiModulePtr, PostNamazu.FrameworkPtr); + GetUIModulePtr = SigScanner.ScanText("E8 * * * * 80 7B 1D 01", nameof(GetUIModulePtr)); // FFXIVClientStructs/FFXIV/Client/Game/GameMain.cs - var mapIDOffset = 0x40B8; // to-do:自动获取 - // var mapIDOffset = SigScanner.Read(SigScanner.ScanText("44 89 81 ? ? ? ? 0F B7 84 24", "mapIDOffset") + 3); - MapIDPtr = SigScanner.ScanText("48 8D 0D * * * * 0F B6 55 ?? 24", "GameMainPtr") + mapIDOffset; + IntPtr gameMainPtr; + gameMainPtr = SigScanner.ScanText("48 8D 0D * * * * 0F B6 55 ?? 24", nameof(gameMainPtr)); + + // GameMain.CurrentContentFinderConditionId + // 7.2/7.3: offset = 0x40AC + // 进出副本时写入 id: + // 66 89 81 AC 40 00 00 mov [rcx+40ACh], ax + ushort contentFinderConditionIdOffset; + contentFinderConditionIdOffset = SigScanner.Read(SigScanner.ScanText("66 89 81 ? ? ? ? 66 85 C0 74 ?", nameof(contentFinderConditionIdOffset)) + 3); + + ContentFinderConditionIdPtr = gameMainPtr + contentFinderConditionIdOffset; } private void GetWayMarkSlotOffset() @@ -50,9 +68,8 @@ private void GetWayMarkSlotOffset() var Case0x11 = UIModuleSwitch.Case(0x11); var offset = SigScanner.Read(Case0x11 + 14); - //var UIModulePtr = SigScanner.Read(UIModulePtrPtr); - var UIModule = UIModulePtr; - var FastCallAddressPtr = SigScanner.Read(UIModule) + offset; + var uiModule = UIModulePtr; + var FastCallAddressPtr = SigScanner.Read(uiModule) + offset; var FastCallAddress = SigScanner.Read(FastCallAddressPtr); //.text:00000001405BA9A0 @@ -62,16 +79,13 @@ private void GetWayMarkSlotOffset() //.text:00000001405BA9A7 sub_1405BA9A0 endp WayMarkSlotOffset = SigScanner.Read(FastCallAddress + 3); } + public IntPtr GetWaymarkDataPointerForSlot(uint slotNum) { - //var g_Framework_2 = MemoryService.Read(g_Framework_2_Ptr); - //var UIModule = MemoryService.Read(g_Framework_2 + 0x29F8); - //var UIModulePtr = SigScanner.Read(UIModulePtrPtr); - var UIModule = UIModulePtr; - - var WayMarkSlotPtr = UIModule + WayMarkSlotOffset; - var WaymarkDataPointer = WayMarkSlotPtr + (PostNamazu.IsCN ? 64 : 72) + (int)(104 * (slotNum - 1)); - return WaymarkDataPointer; + var uiModulePtr = UIModulePtr; + var wayMarkSlotPtr = uiModulePtr + WayMarkSlotOffset; + var waymarkDataPtr = wayMarkSlotPtr + WaymarkDataOffset + (int)(FieldMarkerPresetSize * (slotNum - 1)); + return waymarkDataPtr; } /// @@ -80,8 +94,8 @@ public IntPtr GetWaymarkDataPointerForSlot(uint slotNum) /// 标点合集对象 private void DoInsertPreset(WayMarks waymarks) { - if (waymarks.MapID is > 1000 or 0) - waymarks.MapID = SigScanner.Read(MapIDPtr); + if (waymarks.MapID is > 2000 or 0) + waymarks.MapID = SigScanner.Read(ContentFinderConditionIdPtr); if (waymarks.MapID == 0) { Log(L.Get("Preset/MapIdIllegal")); @@ -91,8 +105,8 @@ private void DoInsertPreset(WayMarks waymarks) IntPtr SlotOffset; - var pattern = @"^Slot0?(\d{1,2})$"; - var match = Regex.Match(waymarks.Name, pattern,RegexOptions.IgnoreCase); + var pattern = @"^Slot\s*(\d+)$"; + var match = Regex.Match(waymarks.Name.Trim(), pattern,RegexOptions.IgnoreCase); if (match.Success && uint.TryParse(match.Groups[1].Value, out var slotNum) && slotNum is > 0 and <= 30) SlotOffset = GetWaymarkDataPointerForSlot(slotNum); @@ -124,6 +138,7 @@ public void DoInsertPreset(string waymarksStr) break; } } + /// /// 构造预设结构,从0号头子的PPR抄来的 /// @@ -131,6 +146,17 @@ public void DoInsertPreset(string waymarksStr) /// byte[]预设结构 public byte[] ConstructGamePreset(WayMarks waymarks) { + /* + [StructLayout(LayoutKind.Explicit, Size = 0x68)] + public partial struct FieldMarkerPreset + { + [FieldOffset(0x00), FixedSizeArray] internal FixedSizeArray8 _markers; + [FieldOffset(0x60)] public byte ActiveMarkers; + [FieldOffset(0x62)] public ushort ContentFinderConditionId; + [FieldOffset(0x64)] public int Timestamp; + } + */ + // List is easy because we can just push data on to it. var byteData = new List(); byte activeMask = 0x00; @@ -144,22 +170,23 @@ public byte[] ConstructGamePreset(WayMarks waymarks) if (waymark.Active) activeMask |= 0b10000000; } + // 0x60 byteData.Add(activeMask); - // Reserved byte. + // 0x61 Reserved byte. byteData.Add((byte)0x00); - // Territory ID. + // 0x62 ContentFinderCondition ID. byteData.AddRange(BitConverter.GetBytes(waymarks.MapID)); - // Time last modified. + // 0x64 Time last modified. var Time = new DateTimeOffset(DateTimeOffset.Now.UtcDateTime); byteData.AddRange(BitConverter.GetBytes((Int32)Time.ToUnixTimeSeconds())); // Shouldn't ever come up with the wrong length, but just in case... - if (byteData.Count != 104) + if (byteData.Count != FieldMarkerPresetSize) { - throw new Exception("Error in ConstructGamePreset(): Constructed byte array was of an unexpected length."); + throw new Exception($"Error in {nameof(ConstructGamePreset)}(): Constructed byte array was of an unexpected length ({byteData.Count})."); } // Send it out. diff --git a/PostNamazu/Common/Localization/CoreLocalizations.cs b/PostNamazu/Common/Localization/CoreLocalizations.cs index 099a18c..2626f72 100644 --- a/PostNamazu/Common/Localization/CoreLocalizations.cs +++ b/PostNamazu/Common/Localization/CoreLocalizations.cs @@ -81,6 +81,13 @@ public static class CoreLocalizations [Localized("Exception when executing action {0}: \n{1}", "执行 {0} 动作时遇到错误:{1}")] private static readonly string doActionFail; + [Localized("FFXIV entrance function found ({0}).", "找到 FFXIV 入口函数({0})。")] + private static readonly string entranceFound; + + [Localized("Specified FFXIV entrance functions not found, temporarily using the default DX11 function instead. Note that this may cause compatibility issues.", + "未找到指定的 FFXIV 入口函数,临时使用默认的 DX11 函数代替。注意这可能导致兼容性问题。")] + private static readonly string entranceNotFound; + [Localized("Unable to start listening on HTTP port {0}: \n{1}", "无法在 {0} 端口启动监听:\n{1}")] private static readonly string httpException; @@ -131,10 +138,14 @@ public static class CoreLocalizations [Localized("Set region to Chinese server.", "已设置为国服。")] private static readonly string xivDetectRegionCN; + [Localized("Unable to detect region and other necessary data. If the game has recently updated, please wait for the plugin to be updated.", + "无法检测到区域等关键信息。如果游戏此前更新了版本,请等待插件更新。")] + private static readonly string xivDetectRegionFail; + [Localized("Set region to global server.", "已设置为国际服。")] private static readonly string xivDetectRegionGlobal; - [Localized("Detected GLOBAL key: {0}", "检测到GLOBAL密钥:{0}")] + [Localized("Detected GLOBAL key: {0}", "检测到 GLOBAL 密钥:{0}")] private static readonly string xivDetectKey; [Localized("Failed to find memory signature for Framework, some features will not be available. The plugin may need to be updated. Exception: {0}", @@ -150,11 +161,7 @@ public static class CoreLocalizations [Localized("Error when injecting into FFXIV process, retry later: \n{0}", "注入 FFXIV 进程时发生错误,正在重试:\n{0}")] private static readonly string xivProcInjectException; - [Localized("Unable to inject into the current FFXIV process, it may have already been injected by another process. Please try restarting the game.", - "无法注入当前进程,可能是已经被其他进程注入了,请尝试重启游戏。")] - private static readonly string xivProcInjectFail; - - [Localized("Failed to connect to FFXIV process {0}:\n{1}", "无法连接至FFXIV进程 {0}:\n{1}")] + [Localized("Failed to connect to FFXIV process {0}:\n{1}", "无法连接至 FFXIV 进程 {0}:\n{1}")] private static readonly string xivProcInjectFailWithError; [Localized("Switched to FFXIV process {0}.", "已切换至 FFXIV 进程 {0}。")] @@ -212,10 +219,10 @@ public static class CoreLocalizations private static readonly string relAddressingFormatError; [Localized("Scanned{0} and found {1} memory signatures, unable to determine a unique location.", - "扫描{0}发现了 {1} 个内存签名,无法确定唯一位置。")] + "扫描{0} 发现了 {1} 个内存签名,无法确定唯一位置。")] private static readonly string resultMultiple; - [Localized("Scanned{0} and did not find the required memory signatures.", "扫描{0}未找到所需的内存签名。")] + [Localized("Scanned{0} and did not find the required memory signatures.", "扫描{0} 未找到所需的内存签名。")] private static readonly string resultNone; } } diff --git a/PostNamazu/Models/WayMarks.cs b/PostNamazu/Models/WayMarks.cs index b3992da..0ec1dbe 100644 --- a/PostNamazu/Models/WayMarks.cs +++ b/PostNamazu/Models/WayMarks.cs @@ -9,6 +9,7 @@ namespace PostNamazu.Models public class WayMarks : IEnumerable { public string Name { get; set; } + /// 实际为 ContentFinderCondition ID,为保持标点 JSON 格式兼容性而写作 MapID。 public ushort MapID { get; set; } public Waymark A { get; set; } public Waymark B { get; set; } diff --git a/PostNamazu/PostNamazu.cs b/PostNamazu/PostNamazu.cs index b4ba120..c68241b 100644 --- a/PostNamazu/PostNamazu.cs +++ b/PostNamazu/PostNamazu.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Reflection; using System.Windows.Forms; +using static PostNamazu.Common.SigScanner; namespace PostNamazu { @@ -268,13 +269,46 @@ internal bool GetOffsets() PluginUi.Log(L.Get("PostNamazu/sigScanning")); SigScanner = new SigScanner(FFXIV); - try + _entrancePtr = IntPtr.Zero; + if (_entrancePtr == IntPtr.Zero) + { + try // 7.3h1 global: 0x1400CDDD0 TaskUpdateInputUI 原始 + { + _entrancePtr = SigScanner.ScanText("4C 8B DC 53 55 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 84 24 ? ? ? ? 48 83 B9 ? ? ? ? ? 48 8B DA 48 8B E9 0F 84"); + PluginUi.Log(L.Get("PostNamazu/entranceFound", "7.3_raw")); + } + catch { } + } + if (_entrancePtr == IntPtr.Zero) { - _entrancePtr = SigScanner.ScanText("4C 8B DC 56 41 57 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 84 24 ? ? ? ? 48 83 B9 ? ? ? ? ? 4C 8B FA"); // 7.0-7.2 + try // 7.2 TaskUpdateInputUI 原始 + { + _entrancePtr = SigScanner.ScanText("4C 8B DC 56 41 57 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 84 24 ? ? ? ? 48 83 B9 ? ? ? ? ? 4C 8B FA"); + PluginUi.Log(L.Get("PostNamazu/entranceFound", "7.2_raw")); + } + catch { } + } + if (_entrancePtr == IntPtr.Zero) + { + try // 7.3 交叉引用,防止冲突 + { + _entrancePtr = SigScanner.ScanText(new SigPatternInfo("48 8B C6 33 D2 48 89 87 ? ? ? ? 45 33 C0 4C 8D B7 ? ? ? ? 8D 4A ?", 0x129)); + PluginUi.Log(L.Get("PostNamazu/entranceFound", "7.3_xref")); + } + catch { } + } + if (_entrancePtr == IntPtr.Zero) + { + try // 7.2 交叉引用,防止冲突 + { + _entrancePtr = SigScanner.ScanText(new SigPatternInfo("33 D2 45 33 C0 4C 8B F8 8D 4A ? E8 ? ? ? ? 48 8D 0D", 0x13A)); + PluginUi.Log(L.Get("PostNamazu/entranceFound", "7.2_xref")); + } + catch { } } - catch + if (_entrancePtr == IntPtr.Zero) { - _entrancePtr = IntPtr.Zero; // 7.3 临时修复,使用默认方式注入 + PluginUi.Log(L.Get("PostNamazu/entranceNotFound")); } try { @@ -284,7 +318,7 @@ internal bool GetOffsets() return true; } catch (ArgumentException) { - PluginUi.Log(L.Get("PostNamazu/xivProcInjectFail")); + PluginUi.Log(L.Get("PostNamazu/xivDetectMemRegionFail")); } return false; } diff --git a/PostNamazu/PostNamazu.csproj b/PostNamazu/PostNamazu.csproj index 2db9c51..de3c01d 100644 --- a/PostNamazu/PostNamazu.csproj +++ b/PostNamazu/PostNamazu.csproj @@ -8,7 +8,7 @@ x64 disable - 1.3.6.5 + 1.3.6.6