refactor: update to sdk and use DalamudPackager

This commit is contained in:
Anna 2020-12-27 12:13:57 -05:00
parent a3affe916a
commit d14f3fcd2c
Signed by: anna
GPG Key ID: 0B391D8F06FCD9E0
9 changed files with 117 additions and 183 deletions

1
.gitignore vendored
View File

@ -34,6 +34,7 @@ bld/
# Visual Studio 2015/2017 cache/options directory
.vs/
.idea/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

View File

@ -4,15 +4,15 @@ using System.Linq;
namespace GoodMemory {
public static class ActionTypeExt {
private static readonly ActionType[] VALID = (ActionType[])Enum.GetValues(typeof(ActionType));
private static readonly ActionType[] Valid = (ActionType[])Enum.GetValues(typeof(ActionType));
public static bool IsValidAction(ItemAction action) {
public static bool IsValidAction(ItemAction? action) {
if (action == null || action.RowId == 0) {
return false;
}
ActionType type = (ActionType)action.Type;
return VALID.Contains(type);
var type = (ActionType)action.Type;
return Valid.Contains(type);
}
}

View File

@ -0,0 +1,10 @@
<Project>
<Target Name="PackagePlugin" AfterTargets="Build" Condition="'$(Configuration)' == 'Release'">
<DalamudPackager ProjectDir="$(ProjectDir)"
OutputPath="$(OutputPath)"
AssemblyName="$(AssemblyName)"
VersionComponents="3"
ManifestType="yaml"
MakeZip="true"/>
</Target>
</Project>

View File

@ -4,58 +4,57 @@ using System.Runtime.InteropServices;
namespace GoodMemory {
public class GameFunctions {
private readonly Plugin plugin;
private Plugin Plugin { get; }
private delegate byte HasItemActionUnlockedDelegate(long itemActionId);
private readonly HasItemActionUnlockedDelegate hasItemActionUnlocked;
private readonly HasItemActionUnlockedDelegate _hasItemActionUnlocked;
private delegate byte HasCardDelegate(IntPtr localPlayer, ushort cardId);
private readonly HasCardDelegate hasCard;
private readonly IntPtr cardStaticAddr;
private readonly HasCardDelegate _hasCard;
private readonly IntPtr _cardStaticAddr;
public GameFunctions(Plugin plugin) {
this.plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
this.Plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
IntPtr hasIAUnlockedPtr = plugin.Interface.TargetModuleScanner.ScanText("48 83 EC 28 E8 ?? ?? ?? ?? 48 85 C0 0F 84 ?? ?? ?? ??");
IntPtr hasCardPtr = plugin.Interface.TargetModuleScanner.ScanText("40 53 48 83 EC 20 48 8B D9 66 85 D2 74 ??");
this.cardStaticAddr = plugin.Interface.TargetModuleScanner.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 84 C0 74 ?? 48 8B 53 ?? 48 8D 4B ?? 48 83 C2 0C 48 8D 14 ?? E8 ?? ?? ?? ?? 40 FE C7 40 3A FD 72 ?? 48 8B 5C 24 ??");
var hasIaUnlockedPtr = plugin.Interface.TargetModuleScanner.ScanText("48 83 EC 28 E8 ?? ?? ?? ?? 48 85 C0 0F 84 ?? ?? ?? ??");
var hasCardPtr = plugin.Interface.TargetModuleScanner.ScanText("40 53 48 83 EC 20 48 8B D9 66 85 D2 74 ??");
this._cardStaticAddr = plugin.Interface.TargetModuleScanner.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 84 C0 74 ?? 48 8B 53 ?? 48 8D 4B ?? 48 83 C2 0C 48 8D 14 ?? E8 ?? ?? ?? ?? 40 FE C7 40 3A FD 72 ?? 48 8B 5C 24 ??");
if (hasIAUnlockedPtr == IntPtr.Zero || hasCardPtr == IntPtr.Zero || this.cardStaticAddr == IntPtr.Zero) {
if (hasIaUnlockedPtr == IntPtr.Zero || hasCardPtr == IntPtr.Zero || this._cardStaticAddr == IntPtr.Zero) {
throw new ApplicationException("Could not get pointers for game functions");
}
this.hasItemActionUnlocked = Marshal.GetDelegateForFunctionPointer<HasItemActionUnlockedDelegate>(hasIAUnlockedPtr);
this.hasCard = Marshal.GetDelegateForFunctionPointer<HasCardDelegate>(hasCardPtr);
this._hasItemActionUnlocked = Marshal.GetDelegateForFunctionPointer<HasItemActionUnlockedDelegate>(hasIaUnlockedPtr);
this._hasCard = Marshal.GetDelegateForFunctionPointer<HasCardDelegate>(hasCardPtr);
}
public bool HasAcquired(Item item) {
ItemAction action = item.ItemAction.Value;
var action = item.ItemAction.Value;
if (action == null) {
return false;
}
ActionType type = (ActionType)action.Type;
var type = (ActionType)action.Type;
if (type == ActionType.Cards) {
uint cardId = item.AdditionalData;
TripleTriadCard card = this.plugin.Interface.Data.GetExcelSheet<TripleTriadCard>().GetRow(cardId);
if (card == null) {
return false;
}
return this.HasCard((ushort)card.RowId);
if (type != ActionType.Cards) {
return this.HasItemActionUnlocked(action.RowId);
}
return this.HasItemActionUnlocked(action.RowId);
var cardId = item.AdditionalData;
var card = this.Plugin.Interface.Data.GetExcelSheet<TripleTriadCard>().GetRow(cardId);
return card != null && this.HasCard((ushort)card.RowId);
}
private bool HasItemActionUnlocked(long itemActionId) {
return this.hasItemActionUnlocked(itemActionId) == 1;
return this._hasItemActionUnlocked(itemActionId) == 1;
}
private bool HasCard(ushort cardId) {
return this.hasCard(this.cardStaticAddr, cardId) == 1;
return this._hasCard(this._cardStaticAddr, cardId) == 1;
}
}
}

View File

@ -1,64 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{2B53DAAE-5F20-4EAF-8BC5-97FCE68C1738}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>GoodMemory</RootNamespace>
<AssemblyName>GoodMemory</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="Dalamud">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\Dalamud.dll</HintPath>
</Reference>
<Reference Include="Lumina">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\Lumina.dll</HintPath>
</Reference>
<Reference Include="Lumina.Excel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\Lumina.Excel.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ActionType.cs" />
<Compile Include="GameFunctions.cs" />
<Compile Include="Plugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="GoodMemory.json" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<LangVersion>8</LangVersion>
<Nullable>enable</Nullable>
<AssemblyVersion>1.0.4</AssemblyVersion>
<FileVersion>1.0.4</FileVersion>
<TargetFramework>net48</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="Dalamud, Version=5.2.1.1, Culture=neutral, PublicKeyToken=null">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\Dalamud.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Lumina, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\Lumina.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Lumina.Excel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\Lumina.Excel.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="DalamudPackager" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@ -1,10 +0,0 @@
{
"Author": "ascclemens",
"Name": "Good Memory",
"Description": "Adds an indicator in item tooltips to show whether you have acquired that item.",
"InternalName": "GoodMemory",
"AssemblyVersion": "1.0.4",
"RepoUrl": "https://git.sr.ht/~jkcclemens/GoodMemory",
"ApplicableVersion": "any",
"DalamudApiLevel": 2
}

View File

@ -0,0 +1,8 @@
author: ascclemens
name: GoodMemory
description: |-
Adds an indicator in item tooltips to show whether you have acquired that item.
This indicator is in the item's description near the bottom and only appears for
items that are unlockable, such as orchestrion rolls, minions, etc.
repo_url: https://git.sr.ht/~jkcclemens/GoodMemory

View File

@ -9,14 +9,14 @@ using System.Text;
namespace GoodMemory {
public class Plugin : IDalamudPlugin {
private bool disposedValue;
private bool _disposedValue;
public string Name => "Good Memory";
public DalamudPluginInterface Interface { get; private set; }
private GameFunctions Functions { get; set; }
private readonly IntPtr alloc = Marshal.AllocHGlobal(4096);
private Hook<TooltipDelegate> tooltipHook;
public DalamudPluginInterface Interface { get; private set; } = null!;
private GameFunctions Functions { get; set; } = null!;
private readonly IntPtr _alloc = Marshal.AllocHGlobal(4096);
private Hook<TooltipDelegate>? _tooltipHook;
private unsafe delegate IntPtr TooltipDelegate(IntPtr a1, uint** a2, byte*** a3);
@ -27,46 +27,48 @@ namespace GoodMemory {
}
protected virtual void Dispose(bool disposing) {
if (!this.disposedValue) {
if (disposing) {
this.tooltipHook?.Dispose();
Marshal.FreeHGlobal(this.alloc);
}
this.disposedValue = true;
if (this._disposedValue) {
return;
}
if (disposing) {
this._tooltipHook?.Dispose();
Marshal.FreeHGlobal(this._alloc);
}
this._disposedValue = true;
}
public void Dispose() {
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
Dispose(true);
GC.SuppressFinalize(this);
}
private void SetUpHook() {
IntPtr tooltipPtr = this.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 50 48 8B 42 ??");
var tooltipPtr = this.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 50 48 8B 42 ??");
if (tooltipPtr == IntPtr.Zero) {
throw new ApplicationException("Could not set up tooltip hook because of null pointer");
}
unsafe {
this.tooltipHook = new Hook<TooltipDelegate>(tooltipPtr, new TooltipDelegate(this.OnTooltip));
this._tooltipHook = new Hook<TooltipDelegate>(tooltipPtr, new TooltipDelegate(this.OnTooltip));
}
this.tooltipHook.Enable();
this._tooltipHook.Enable();
}
private unsafe void TooltipLogic(IntPtr a1, uint** a2, byte*** a3) {
private unsafe void TooltipLogic(uint** a2, byte*** a3) {
// this can be replaced with a mid-func hook when reloaded hooks is in dalamud
// but for now, do the same logic the func does and replace the text after
uint* v3 = *(a2 + 4);
uint v9 = *(v3 + 4);
var v3 = *(a2 + 4);
var v9 = *(v3 + 4);
if ((v9 & 2) == 0) {
return;
}
ulong itemId = this.Interface.Framework.Gui.HoveredItem;
var itemId = this.Interface.Framework.Gui.HoveredItem;
if (itemId > 2_000_000) {
return;
}
@ -75,19 +77,19 @@ namespace GoodMemory {
itemId -= 1_000_000;
}
Item item = this.Interface.Data.GetExcelSheet<Item>().GetRow((uint)itemId);
var item = this.Interface.Data.GetExcelSheet<Item>().GetRow((uint)itemId);
if (item == null) {
return;
}
// get the pointer to the text
byte** startPtr = *(a3 + 4) + 13;
var startPtr = *(a3 + 4) + 13;
// get the text pointer
byte* start = *startPtr;
var start = *startPtr;
// work around function being called twice
if (start == (byte*)this.alloc) {
if (start == (byte*)this._alloc) {
return;
}
@ -95,7 +97,7 @@ namespace GoodMemory {
// Faded Copies
if (item.FilterGroup == 12 && item.ItemUICategory.Value?.RowId == 94 && item.LevelItem.Value?.RowId == 1) {
Item[] recipeResults = this.Interface.Data.GetExcelSheet<Recipe>()
var recipeResults = this.Interface.Data.GetExcelSheet<Recipe>()
.Where(recipe => recipe.UnkStruct5.Any(ritem => ritem.ItemIngredient == item.RowId))
.Select(recipe => recipe.ItemResult.Value)
.Where(result => result != null)
@ -103,16 +105,16 @@ namespace GoodMemory {
overwrite = ReadString(start);
foreach (Item result in recipeResults) {
ItemAction resultAction = result.ItemAction?.Value;
foreach (var result in recipeResults) {
var resultAction = result.ItemAction?.Value;
if (!ActionTypeExt.IsValidAction(resultAction)) {
continue;
}
Debug.Assert(resultAction != null, nameof(resultAction) + " != null");
uint orchId = resultAction.Data[0];
Orchestrion orch = this.Interface.Data.GetExcelSheet<Orchestrion>().GetRow(orchId);
uint orchId = resultAction!.Data[0];
var orch = this.Interface.Data.GetExcelSheet<Orchestrion>().GetRow(orchId);
if (orch == null) {
continue;
}
@ -120,7 +122,7 @@ namespace GoodMemory {
this.AppendIfAcquired(ref overwrite, result, orch.Name);
}
} else {
ItemAction action = item.ItemAction?.Value;
var action = item.ItemAction?.Value;
if (!ActionTypeExt.IsValidAction(action)) {
return;
@ -134,23 +136,23 @@ namespace GoodMemory {
}
// write our replacement text into our own managed memory (4096 bytes)
WriteString((byte*)this.alloc, overwrite, true);
WriteString((byte*)this._alloc, overwrite, true);
// overwrite the original pointer with our own
*startPtr = (byte*)this.alloc;
*startPtr = (byte*)this._alloc;
}
private unsafe IntPtr OnTooltip(IntPtr a1, uint** a2, byte*** a3) {
try {
this.TooltipLogic(a1, a2, a3);
this.TooltipLogic(a2, a3);
} catch (Exception ex) {
PluginLog.Error($"Could not modify tooltip:\n{ex.Message}\n{ex.StackTrace}");
}
return this.tooltipHook.Original(a1, a2, a3);
return this._tooltipHook!.Original(a1, a2, a3);
}
private void AppendIfAcquired(ref string txt, Item item, string name = null) {
private void AppendIfAcquired(ref string txt, Item item, string? name = null) {
string yes;
string no;
string acquired;
@ -158,7 +160,6 @@ namespace GoodMemory {
string parenL;
string parenR;
switch (this.Interface.ClientState.ClientLanguage) {
case Dalamud.ClientLanguage.English:
default:
acquired = "Acquired";
colon = ": ";
@ -193,18 +194,16 @@ namespace GoodMemory {
break;
}
string has = this.Functions.HasAcquired(item) ? yes : no;
if (name != null) {
txt = $"{txt}\n{acquired}{parenL}{name}{parenR}{colon}{has}";
} else {
txt = $"{txt}\n{acquired}{colon}{has}";
}
var has = this.Functions.HasAcquired(item) ? yes : no;
txt = name == null
? $"{txt}\n{acquired}{colon}{has}"
: $"{txt}\n{acquired}{parenL}{name}{parenR}{colon}{has}";
}
private unsafe static string ReadString(byte* ptr) {
int offset = 0;
var offset = 0;
while (true) {
byte b = *(ptr + offset);
var b = *(ptr + offset);
if (b == 0) {
break;
}
@ -216,8 +215,8 @@ namespace GoodMemory {
}
private unsafe static void WriteString(byte* dst, string s, bool finalise = false) {
byte[] bytes = Encoding.UTF8.GetBytes(s);
for (int i = 0; i < bytes.Length; i++) {
var bytes = Encoding.UTF8.GetBytes(s);
for (var i = 0; i < bytes.Length; i++) {
*(dst + i) = bytes[i];
}

View File

@ -1,38 +0,0 @@
using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("GoodMemory")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("GoodMemory")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("2b53daae-5f20-4eaf-8bc5-97fce68c1738")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.4")]
[assembly: AssemblyFileVersion("1.0.4")]
[assembly: NeutralResourcesLanguage("en-GB")]