Skip to content
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
87 changes: 57 additions & 30 deletions PostNamazu/Actions/Preset.cs
Original file line number Diff line number Diff line change
@@ -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<IntPtr>(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<FieldMarkerPreset> _presets
int WaymarkDataOffset = 0x48;

// FFXIVClientStructs/FFXIV/Client/UI/Misc/FieldMarkerModule
// [StructLayout(LayoutKind.Explicit, Size = 0x68)] public partial struct FieldMarkerPreset
int FieldMarkerPresetSize = 0x68;

// 本地化字符串定义
[LocalizationProvider("Preset")]
Expand All @@ -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<IntPtr>(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<UInt16>(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<ushort>(SigScanner.ScanText("66 89 81 ? ? ? ? 66 85 C0 74 ?", nameof(contentFinderConditionIdOffset)) + 3);

ContentFinderConditionIdPtr = gameMainPtr + contentFinderConditionIdOffset;
}

private void GetWayMarkSlotOffset()
Expand All @@ -50,9 +68,8 @@ private void GetWayMarkSlotOffset()
var Case0x11 = UIModuleSwitch.Case(0x11);
var offset = SigScanner.Read<int>(Case0x11 + 14);

//var UIModulePtr = SigScanner.Read<IntPtr>(UIModulePtrPtr);
var UIModule = UIModulePtr;
var FastCallAddressPtr = SigScanner.Read<IntPtr>(UIModule) + offset;
var uiModule = UIModulePtr;
var FastCallAddressPtr = SigScanner.Read<IntPtr>(uiModule) + offset;

var FastCallAddress = SigScanner.Read<IntPtr>(FastCallAddressPtr);
//.text:00000001405BA9A0
Expand All @@ -62,16 +79,13 @@ private void GetWayMarkSlotOffset()
//.text:00000001405BA9A7 sub_1405BA9A0 endp
WayMarkSlotOffset = SigScanner.Read<Int32>(FastCallAddress + 3);
}

public IntPtr GetWaymarkDataPointerForSlot(uint slotNum)
{
//var g_Framework_2 = MemoryService.Read<IntPtr>(g_Framework_2_Ptr);
//var UIModule = MemoryService.Read<IntPtr>(g_Framework_2 + 0x29F8);
//var UIModulePtr = SigScanner.Read<IntPtr>(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;
}

/// <summary>
Expand All @@ -80,8 +94,8 @@ public IntPtr GetWaymarkDataPointerForSlot(uint slotNum)
/// <param name="waymarks">标点合集对象</param>
private void DoInsertPreset(WayMarks waymarks)
{
if (waymarks.MapID is > 1000 or 0)
waymarks.MapID = SigScanner.Read<ushort>(MapIDPtr);
if (waymarks.MapID is > 2000 or 0)
waymarks.MapID = SigScanner.Read<ushort>(ContentFinderConditionIdPtr);
if (waymarks.MapID == 0)
{
Log(L.Get("Preset/MapIdIllegal"));
Expand All @@ -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);
Expand Down Expand Up @@ -124,13 +138,25 @@ public void DoInsertPreset(string waymarksStr)
break;
}
}

/// <summary>
/// 构造预设结构,从0号头子的PPR抄来的
/// </summary>
/// <param name="waymark">标点</param>
/// <returns>byte[]预设结构</returns>
public byte[] ConstructGamePreset(WayMarks waymarks)
{
/*
[StructLayout(LayoutKind.Explicit, Size = 0x68)]
public partial struct FieldMarkerPreset
{
[FieldOffset(0x00), FixedSizeArray] internal FixedSizeArray8<GamePresetPoint> _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>();
byte activeMask = 0x00;
Expand All @@ -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.
Expand Down
23 changes: 15 additions & 8 deletions PostNamazu/Common/Localization/CoreLocalizations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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}",
Expand All @@ -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}。")]
Expand Down Expand Up @@ -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;
}
}
1 change: 1 addition & 0 deletions PostNamazu/Models/WayMarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace PostNamazu.Models
public class WayMarks : IEnumerable<Waymark>
{
public string Name { get; set; }
/// <summary> 实际为 ContentFinderCondition ID,为保持标点 JSON 格式兼容性而写作 MapID。</summary>
public ushort MapID { get; set; }
public Waymark A { get; set; }
public Waymark B { get; set; }
Expand Down
44 changes: 39 additions & 5 deletions PostNamazu/PostNamazu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using static PostNamazu.Common.SigScanner;

namespace PostNamazu
{
Expand Down Expand Up @@ -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 {
Expand All @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion PostNamazu/PostNamazu.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<PlatformTarget>x64</PlatformTarget>
<Nullable>disable</Nullable>
<AssemblyVersion></AssemblyVersion>
<Version>1.3.6.5</Version>
<Version>1.3.6.6</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
Loading