This commit is contained in:
attick 2019-11-19 01:29:53 -05:00
commit 9836db6b90
25 changed files with 521 additions and 129 deletions

View file

@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Target">
<PlatformTarget>AnyCPU</PlatformTarget>
<TargetFramework>net471</TargetFramework>
<LangVersion>7.2</LangVersion>
<TargetFramework>net48</TargetFramework>
<LangVersion>8.0</LangVersion>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<PropertyGroup Label="Build">
@ -14,10 +14,13 @@
</PropertyGroup>
<PropertyGroup Label="Feature">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AssemblyVersion>2.4.0.0</AssemblyVersion>
<FileVersion>2.4.0.0</FileVersion>
<AssemblyVersion>4.1.0.3</AssemblyVersion>
<FileVersion>4.1.0.3</FileVersion>
<Description>XIVLauncher addon injection</Description>
<Version>2.4.0</Version>
<Version>4.1.0.3</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile></DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="EasyHook" Version="2.7.6270" />
@ -26,4 +29,4 @@
<ItemGroup>
<ProjectReference Include="..\Dalamud\Dalamud.csproj" />
</ItemGroup>
</Project>
</Project>

View file

@ -30,7 +30,7 @@ namespace Dalamud.Injector {
case -2:
process = Process.Start(
"C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\ffxiv_dx11.exe",
"DEV.TestSID=5fa077c389a61c4a45ea35153162753d7cdb34268cc38c9e206859a7 DEV.UseSqPack=1 DEV.DataPathType=1 DEV.LobbyHost01=127.0.0.1 DEV.LobbyPort01=54994 DEV.LobbyHost02=127.0.0.1 DEV.LobbyPort02=54994 DEV.LobbyHost03=127.0.0.1 DEV.LobbyPort03=54994 DEV.LobbyHost04=127.0.0.1 DEV.LobbyPort04=54994 DEV.LobbyHost05=127.0.0.1 DEV.LobbyPort05=54994 DEV.LobbyHost06=127.0.0.1 DEV.LobbyPort06=54994 DEV.LobbyHost07=127.0.0.1 DEV.LobbyPort07=54994 DEV.LobbyHost08=127.0.0.1 DEV.LobbyPort08=54994 SYS.Region=0 language=1 version=1.0.0.0 DEV.MaxEntitledExpansionID=2 DEV.GMServerHost=127.0.0.1 DEV.GameQuitMessageBox=0");
"DEV.TestSID=0 DEV.UseSqPack=1 DEV.DataPathType=1 DEV.LobbyHost01=127.0.0.1 DEV.LobbyPort01=54994 DEV.LobbyHost02=127.0.0.1 DEV.LobbyPort02=54994 DEV.LobbyHost03=127.0.0.1 DEV.LobbyPort03=54994 DEV.LobbyHost04=127.0.0.1 DEV.LobbyPort04=54994 DEV.LobbyHost05=127.0.0.1 DEV.LobbyPort05=54994 DEV.LobbyHost06=127.0.0.1 DEV.LobbyPort06=54994 DEV.LobbyHost07=127.0.0.1 DEV.LobbyPort07=54994 DEV.LobbyHost08=127.0.0.1 DEV.LobbyPort08=54994 SYS.Region=0 language=1 version=1.0.0.0 DEV.MaxEntitledExpansionID=2 DEV.GMServerHost=127.0.0.1 DEV.GameQuitMessageBox=0");
break;
default:
process = Process.GetProcessById(pid);

View file

@ -2,7 +2,9 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.DiscordBot;
using Dalamud.Game;
using Dalamud.Game.Chat;
@ -44,6 +46,8 @@ namespace Dalamud {
public readonly DalamudConfiguration Configuration;
internal readonly WinSockHandlers WinSock2;
public Dalamud(DalamudStartInfo info) {
this.StartInfo = info;
this.Configuration = DalamudConfiguration.Load(info.ConfigurationPath);
@ -73,7 +77,9 @@ namespace Dalamud {
this.PluginManager = new PluginManager(this, info.PluginDirectory, info.DefaultPluginDirectory);
this.IconReplacer = new IconReplacer(this, this.sigScanner);
this.WinSock2 = new WinSockHandlers();
try {
this.PluginManager.LoadPlugins();
} catch (Exception ex) {
@ -88,7 +94,8 @@ namespace Dalamud {
this.BotManager.Start();
this.IconReplacer.Enable();
if (this.Configuration.ComboPresets != CustomComboPreset.None)
this.IconReplacer.Enable();
}
public void Unload() {
@ -106,7 +113,10 @@ namespace Dalamud {
this.unloadSignal.Dispose();
this.IconReplacer.Dispose();
this.WinSock2.Dispose();
if (this.Configuration.ComboPresets != CustomComboPreset.None)
this.IconReplacer.Dispose();
}
private void SetupCommands() {
@ -170,6 +180,16 @@ namespace Dalamud {
CommandManager.AddHandler("/xlbotjoin", new CommandInfo(OnBotJoinCommand) {
HelpMessage = "Add the XIVLauncher discord bot you set up to your server."
});
CommandManager.AddHandler("/xlbgmset", new CommandInfo(OnBgmSetCommand)
{
HelpMessage = "Set the Game background music. Usage: /bgmset <BGM ID>"
});
CommandManager.AddHandler("/xlitem", new CommandInfo(OnItemLinkCommand)
{
HelpMessage = "Link an item by name. Usage: /item <Item name>"
});
}
private void OnUnloadCommand(string command, string arguments) {
@ -196,7 +216,7 @@ namespace Dalamud {
var msg = string.Join(" ", parts.Take(1).ToArray());
Framework.Gui.Chat.PrintChat(new XivChatEntry {
Message = msg,
MessageBytes = Encoding.UTF8.GetBytes(msg),
Name = "Xiv Launcher",
Type = chatType
});
@ -346,7 +366,6 @@ namespace Dalamud {
var argumentsParts = arguments.Split();
switch (argumentsParts[0]) {
/* Sorry!
case "setall": {
foreach (var value in Enum.GetValues(typeof(CustomComboPreset)).Cast<CustomComboPreset>()) {
if (value == CustomComboPreset.None)
@ -394,7 +413,7 @@ namespace Dalamud {
}
}
break;
*/
case "list": {
foreach (var value in Enum.GetValues(typeof(CustomComboPreset)).Cast<CustomComboPreset>()) {
if (this.Configuration.ComboPresets.HasFlag(value))
@ -418,5 +437,63 @@ namespace Dalamud {
Framework.Gui.Chat.Print(
"The XIVLauncher discord bot was not set up correctly or could not connect to discord. Please check the settings and the FAQ.");
}
private void OnBgmSetCommand(string command, string arguments)
{
Framework.Network.InjectBgmTest(int.Parse(arguments));
}
private void OnItemLinkCommand(string command, string arguments) {
Task.Run(async () => {
try {
dynamic results = await XivApi.Search(arguments, "Item", 1);
var itemId = (short) results.Results[0].ID;
var itemName = (string)results.Results[0].Name;
var hexData = new byte[] {
0x02, 0x13, 0x06, 0xFE, 0xFF, 0xF3, 0xF3, 0xF3, 0x03, 0x02, 0x27, 0x07, 0x03, 0xF2, 0x3A, 0x2F,
0x02, 0x01, 0x03, 0x02, 0x13, 0x06, 0xFE, 0xFF, 0xFF, 0x7B, 0x1A, 0x03, 0xEE, 0x82, 0xBB, 0x02,
0x13, 0x02, 0xEC, 0x03
};
var endTag = new byte[] {
0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03, 0x02, 0x13, 0x02, 0xEC, 0x03
};
BitConverter.GetBytes(itemId).Reverse().ToArray().CopyTo(hexData, 14);
hexData = hexData.Concat(Encoding.UTF8.GetBytes(itemName)).Concat(endTag).ToArray();
Framework.Gui.Chat.PrintChat(new XivChatEntry {
MessageBytes = hexData
});
}
catch {
Framework.Gui.Chat.PrintError("Could not find item.");
}
});
}
public static byte[] StringToByteArray(String value)
{
byte[] bytes = new byte[value.Length * sizeof(char)];
Buffer.BlockCopy(value.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
public static String ByteArrayToString(byte[] value)
{
var chars = new char[value.Length / sizeof(char)];
var atValue = 0;
for (var i = 0; i < chars.Length; i++) {
chars[i] = BitConverter.ToChar(value, atValue);
atValue += 2;
}
return new string(chars);
}
}
}

View file

@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Target">
<PlatformTarget>AnyCPU</PlatformTarget>
<TargetFramework>net471</TargetFramework>
<LangVersion>7.3</LangVersion>
<TargetFramework>net48</TargetFramework>
<LangVersion>8.0</LangVersion>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<PropertyGroup Label="Build">
@ -14,9 +14,9 @@
</PropertyGroup>
<PropertyGroup Label="Feature">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AssemblyVersion>2.2.0.0</AssemblyVersion>
<Version>2.2.0</Version>
<FileVersion>2.2.0.0</FileVersion>
<AssemblyVersion>4.1.0.3</AssemblyVersion>
<Version>4.1.0.3</Version>
<FileVersion>4.1.0.3</FileVersion>
</PropertyGroup>
<ItemGroup Label="Resources">
<None Include="$(SolutionDir)/Resources/**/*" CopyToOutputDirectory="PreserveNewest" Visible="false" />
@ -68,4 +68,4 @@
<ItemGroup>
<Folder Include="Configuration\" />
</ItemGroup>
</Project>
</Project>

View file

@ -231,13 +231,13 @@ namespace Dalamud.DiscordBot {
wasOutgoingTell = true;
}
var chatTypeConfig =
this.config.ChatTypeConfigurations.FirstOrDefault(typeConfig => typeConfig.ChatType == type);
var chatTypeConfigs =
this.config.ChatTypeConfigurations.Where(typeConfig => typeConfig.ChatType == type);
if (chatTypeConfig == null)
if (!chatTypeConfigs.Any())
return;
var channel = await GetChannel(chatTypeConfig.Channel);
var channels = chatTypeConfigs.Select(c => GetChannel(c.Channel).GetAwaiter().GetResult());
var senderSplit = sender.Split(new[] {this.worldIcon}, StringSplitOptions.None);
@ -260,58 +260,89 @@ namespace Dalamud.DiscordBot {
var avatarUrl = "";
var lodestoneId = 0;
try {
dynamic charCandidates = await XivApi.GetCharacterSearch(sender, world);
if (!this.config.DisableEmbeds) {
try
{
dynamic charCandidates = await XivApi.GetCharacterSearch(sender, world);
if (charCandidates.Results.Count > 0) {
avatarUrl = charCandidates.Results[0].Avatar;
lodestoneId = charCandidates.Results[0].ID;
if (charCandidates.Results.Count > 0)
{
avatarUrl = charCandidates.Results[0].Avatar;
lodestoneId = charCandidates.Results[0].ID;
}
}
} catch (Exception ex) {
Log.Error(ex, "Could not get XIVAPI character search result.");
}
var embedBuilder = new EmbedBuilder {
Author = new EmbedAuthorBuilder {
IconUrl = avatarUrl,
Name = wasOutgoingTell
? "You"
: sender + (string.IsNullOrEmpty(world) || string.IsNullOrEmpty(sender)
? ""
: $" on {world}"),
Url = lodestoneId != 0 ? "https://eu.finalfantasyxiv.com/lodestone/character/" + lodestoneId : null
},
Description = message,
Timestamp = DateTimeOffset.Now,
Footer = new EmbedFooterBuilder {Text = type.GetDetails().FancyName},
Color = new Color((uint) (chatTypeConfig.Color & 0xFFFFFF))
};
if (this.config.CheckForDuplicateMessages) {
var recentMsg = this.recentMessages.FirstOrDefault(
msg => msg.Embeds.FirstOrDefault(
embed => embed.Description == embedBuilder.Description &&
embed.Author.HasValue &&
embed.Author.Value.Name == embedBuilder.Author.Name &&
embed.Timestamp.HasValue &&
Math.Abs(
(embed.Timestamp.Value.ToUniversalTime().Date -
embedBuilder
.Timestamp.Value.ToUniversalTime().Date)
.Milliseconds) < 15000)
!= null);
if (recentMsg != null) {
Log.Verbose("Duplicate message: [{0}] {1}", embedBuilder.Author.Name, embedBuilder.Description);
this.recentMessages.Remove(recentMsg);
return;
catch (Exception ex)
{
Log.Error(ex, "Could not get XIVAPI character search result.");
}
}
Thread.Sleep(this.config.ChatDelayMs);
await channel.SendMessageAsync(embed: embedBuilder.Build());
var name = wasOutgoingTell
? "You"
: sender + (string.IsNullOrEmpty(world) || string.IsNullOrEmpty(sender)
? ""
: $" on {world}");
for (var chatTypeIndex = 0; chatTypeIndex < chatTypeConfigs.Count(); chatTypeIndex++) {
if (!this.config.DisableEmbeds) {
var embedBuilder = new EmbedBuilder
{
Author = new EmbedAuthorBuilder
{
IconUrl = avatarUrl,
Name = name,
Url = lodestoneId != 0 ? "https://eu.finalfantasyxiv.com/lodestone/character/" + lodestoneId : null
},
Description = message,
Timestamp = DateTimeOffset.Now,
Footer = new EmbedFooterBuilder { Text = type.GetDetails().FancyName },
Color = new Color((uint)(chatTypeConfigs.ElementAt(chatTypeIndex).Color & 0xFFFFFF))
};
if (this.config.CheckForDuplicateMessages)
{
var recentMsg = this.recentMessages.FirstOrDefault(
msg => msg.Embeds.FirstOrDefault(
embed => embed.Description == embedBuilder.Description &&
embed.Author.HasValue &&
embed.Author.Value.Name == embedBuilder.Author.Name &&
embed.Timestamp.HasValue &&
Math.Abs(
(embed.Timestamp.Value.ToUniversalTime().Date -
embedBuilder
.Timestamp.Value.ToUniversalTime().Date)
.Milliseconds) < 15000)
!= null);
if (recentMsg != null)
{
Log.Verbose("Duplicate message: [{0}] {1}", embedBuilder.Author.Name, embedBuilder.Description);
this.recentMessages.Remove(recentMsg);
return;
}
}
await channels.ElementAt(chatTypeIndex).SendMessageAsync(embed: embedBuilder.Build());
} else {
var simpleMessage = $"{name}: {message}";
if (this.config.CheckForDuplicateMessages) {
var recentMsg = this.recentMessages.FirstOrDefault(
msg => msg.Content == simpleMessage);
if (recentMsg != null)
{
Log.Verbose("Duplicate message: {0}", simpleMessage);
this.recentMessages.Remove(recentMsg);
return;
}
}
await channels.ElementAt(chatTypeIndex).SendMessageAsync($"{name}: {message}");
}
}
}
private async Task<IMessageChannel> GetChannel(ChannelConfiguration channelConfig) {
@ -321,7 +352,7 @@ namespace Dalamud.DiscordBot {
}
private string RemoveAllNonLanguageCharacters(string input) {
return Regex.Replace(input, @"[^\p{L} ]", "");
return Regex.Replace(input, @"[^\p{L} ']", "");
}
public void Dispose() {

View file

@ -32,11 +32,14 @@ namespace Dalamud.DiscordBot
public class DiscordFeatureConfiguration
{
public string Token { get; set; }
public ulong OwnerUserId { get; set; }
public bool CheckForDuplicateMessages { get; set; }
public int ChatDelayMs { get; set; }
public bool DisableEmbeds { get; set; }
public ulong OwnerUserId { get; set; }
public List<ChatTypeConfiguration> ChatTypeConfigurations { get; set; }
public ChannelConfiguration CfNotificationChannel { get; set; }

View file

@ -8,7 +8,7 @@ namespace Dalamud.Game.Chat {
public string Name { get; set; } = string.Empty;
public string Message { get; set; } = string.Empty;
public byte[] MessageBytes { get; set; }
public IntPtr Parameters { get; set; }
}

View file

@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@ -65,11 +67,17 @@ namespace Dalamud.Game {
private void ChatOnOnChatMessage(XivChatType type, uint senderId, string sender, ref string message,
ref bool isHandled) {
if (type == XivChatType.Notice && !this.hasSeenLoadingMsg) {
this.dalamud.Framework.Gui.Chat.Print("XIVLauncher in-game addon loaded.");
this.dalamud.Framework.Gui.Chat.Print($"XIVLauncher in-game addon v{Assembly.GetAssembly(typeof(ChatHandlers)).GetName().Version} loaded.");
this.hasSeenLoadingMsg = true;
}
#if !DEBUG
if (!this.hasSeenLoadingMsg)
return;
#endif
var matched = this.rmtRegex.IsMatch(message);
if (matched) {
// This seems to be a RMT ad - let's not show it

View file

@ -32,25 +32,30 @@ namespace Dalamud.Game.ClientState.Actors {
if (index > Length)
return null;
Log.Information("Trying to get actor at {0}", index);
//Log.Information("Trying to get actor at {0}", index);
var tblIndex = Address.ActorTable + 8 + index * 8;
var offset = Marshal.ReadIntPtr(tblIndex);
Log.Information("Actor at {0}", offset.ToString());
//Log.Information("Actor at {0}", offset.ToString());
if (offset == IntPtr.Zero)
throw new Exception($"Actor slot at index {index} is invalid");
return null;
var actorStruct = Marshal.PtrToStructure<Structs.Actor>(offset);
try {
var actorStruct = Marshal.PtrToStructure<Structs.Actor>(offset);
//Log.Debug("ActorTable[{0}]: {1} - {2} - {3}", index, tblIndex.ToString("X"), offset.ToString("X"),
// actorStruct.ObjectKind.ToString());
//Log.Debug("ActorTable[{0}]: {1} - {2} - {3}", index, tblIndex.ToString("X"), offset.ToString("X"),
// actorStruct.ObjectKind.ToString());
switch (actorStruct.ObjectKind) {
case ObjectKind.Player: return new PlayerCharacter(actorStruct);
case ObjectKind.BattleNpc: return new BattleNpc(actorStruct);
default: return new Actor(actorStruct);
switch (actorStruct.ObjectKind)
{
case ObjectKind.Player: return new PlayerCharacter(actorStruct);
case ObjectKind.BattleNpc: return new BattleNpc(actorStruct);
default: return new Actor(actorStruct);
}
} catch (AccessViolationException) {
return null;
}
}
}

View file

@ -32,7 +32,18 @@ namespace Dalamud.Game.ClientState
/// <summary>
/// The local player character, if one is present.
/// </summary>
public PlayerCharacter LocalPlayer { get; private set; }
//public PlayerCharacter LocalPlayer { get; private set; }
public PlayerCharacter LocalPlayer {
get {
var actor = this.Actors[0];
if (actor is PlayerCharacter pc)
return pc;
return null;
}
}
//public PlayerCharacter LocalPlayer => null;
/// <summary>
/// The content ID of the local character.
@ -67,8 +78,7 @@ namespace Dalamud.Game.ClientState
}
private void FrameworkOnOnUpdateEvent(Framework framework) {
LocalPlayer = (PlayerCharacter) this.Actors[0];
Log.Verbose("FRAMEWORK UPDATE");
//LocalPlayer = (PlayerCharacter) this.Actors[0];
}
}
}

View file

@ -13,9 +13,9 @@ namespace Dalamud.Game.ClientState
public IntPtr JobGaugeData { get; set; }
protected override void Setup64Bit(SigScanner sig) {
ActorTable = sig.Module.BaseAddress + 0x1C01D90;
ActorTable = sig.Module.BaseAddress + 0x1BFBA38;
LocalContentId = sig.Module.BaseAddress + 0x1C2E000;
JobGaugeData = sig.Module.BaseAddress + 0x1BFD110;
JobGaugeData = sig.Module.BaseAddress + 0x1BF8110;
}
}
}

View file

@ -90,6 +90,7 @@ namespace Dalamud.Game.Internal {
private bool HandleFrameworkUpdate(IntPtr framework) {
try {
Gui.Chat.UpdateQueue(this);
Network.UpdateQueue(this);
} catch (Exception ex) {
Log.Error(ex, "Exception while handling Framework::Update hook.");
}

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using Dalamud.Game.Chat;
using Dalamud.Game.Internal.Libc;
using Dalamud.Hooking;
@ -9,9 +10,9 @@ using Serilog;
namespace Dalamud.Game.Internal.Gui {
public sealed class ChatGui : IDisposable {
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate void PrintMessageDelegate(IntPtr manager, XivChatType chatType, IntPtr senderName,
private delegate IntPtr PrintMessageDelegate(IntPtr manager, XivChatType chatType, IntPtr senderName,
IntPtr message,
uint senderId, byte isLocal);
uint senderId, IntPtr parameter);
public delegate void OnMessageDelegate(XivChatType type, uint senderId, string sender, ref string message,
ref bool isHandled);
@ -78,13 +79,15 @@ namespace Dalamud.Game.Internal.Gui {
}
}
private void HandlePrintMessageDetour(IntPtr manager, XivChatType chattype, IntPtr pSenderName, IntPtr pMessage,
uint senderid, byte isLocal) {
private IntPtr HandlePrintMessageDetour(IntPtr manager, XivChatType chattype, IntPtr pSenderName, IntPtr pMessage,
uint senderid, IntPtr parameter) {
IntPtr retVal = IntPtr.Zero;
try {
var senderName = StdString.ReadFromPointer(pSenderName);
var message = StdString.ReadFromPointer(pMessage);
//Log.Debug($"HandlePrintMessageDetour {manager} - [{chattype}] [{BitConverter.ToString(Encoding.UTF8.GetBytes(message))}] {message} from {senderName}");
Log.Debug($"HandlePrintMessageDetour {manager} - [{chattype}] [{BitConverter.ToString(Encoding.UTF8.GetBytes(message)).Replace("-", " ")}] {message} from {senderName}");
var originalMessage = string.Copy(message);
@ -93,10 +96,10 @@ namespace Dalamud.Game.Internal.Gui {
OnChatMessage?.Invoke(chattype, senderid, senderName, ref message, ref isHandled);
var messagePtr = pMessage;
OwnedStdString allocatedString = null;
OwnedStdString allocatedString = null;
if (originalMessage != message) {
allocatedString = this.dalamud.Framework.Libc.NewString(message);
allocatedString = this.dalamud.Framework.Libc.NewString(Encoding.UTF8.GetBytes(message));
Log.Debug(
$"HandlePrintMessageDetour String modified: {originalMessage}({messagePtr}) -> {message}({allocatedString.Address})");
messagePtr = allocatedString.Address;
@ -104,7 +107,7 @@ namespace Dalamud.Game.Internal.Gui {
// Print the original chat if it's handled.
if (!isHandled)
this.printMessageHook.Original(manager, chattype, pSenderName, messagePtr, senderid, isLocal);
retVal = this.printMessageHook.Original(manager, chattype, pSenderName, messagePtr, senderid, parameter);
if (this.baseAddress == IntPtr.Zero)
this.baseAddress = manager;
@ -112,8 +115,10 @@ namespace Dalamud.Game.Internal.Gui {
allocatedString?.Dispose();
} catch (Exception ex) {
Log.Error(ex, "Exception on OnChatMessage hook.");
this.printMessageHook.Original(manager, chattype, pSenderName, pMessage, senderid, isLocal);
retVal = this.printMessageHook.Original(manager, chattype, pSenderName, pMessage, senderid, parameter);
}
return retVal;
}
/// <summary>
@ -127,13 +132,13 @@ namespace Dalamud.Game.Internal.Gui {
public void Print(string message) {
PrintChat(new XivChatEntry {
Message = message
MessageBytes = Encoding.UTF8.GetBytes(message)
});
}
public void PrintError(string message) {
PrintChat(new XivChatEntry {
Message = message,
MessageBytes = Encoding.UTF8.GetBytes(message),
Type = XivChatType.Urgent
});
}
@ -146,13 +151,15 @@ namespace Dalamud.Game.Internal.Gui {
var chat = this.chatQueue.Dequeue();
var sender = chat.Name ?? "";
var message = chat.Message ?? "";
var message = chat.MessageBytes ?? new byte[0];
if (this.baseAddress != IntPtr.Zero)
using (var senderVec = framework.Libc.NewString(sender))
using (var messageVec = framework.Libc.NewString(message)) {
using (var senderVec = framework.Libc.NewString(Encoding.UTF8.GetBytes(sender)))
using (var messageVec = framework.Libc.NewString(message))
{
Log.Verbose($"String allocated to {messageVec.Address.ToInt64():X}");
this.printMessageHook.Original(this.baseAddress, chat.Type, senderVec.Address,
messageVec.Address, chat.SenderId, 0);
messageVec.Address, chat.SenderId, chat.Parameters);
}
}
}

View file

@ -86,8 +86,8 @@ namespace Dalamud.Game.Internal.Gui {
//PopulateItemLinkObject = sig.ScanText("48 89 5C 24 08 57 48 83 EC 20 80 7A 06 00 48 8B DA 48 8B F9 74 14 48 8B CA E8 32 03 00 00 48 8B C8 E8 FA F2 B0 FF 8B C8 EB 1D 0F B6 42 14 8B 4A");
//PopulateItemLinkObject = sig.ScanText("48 89 5C 24 08 57 48 83 EC 20 80 7A 06 00 48 8B DA 48 8B F9 74 14 48 8B CA E8 32 03 00 00 48 8B C8 E8 ?? ?? B0 FF 8B C8 EB 1D 0F B6 42 14 8B 4A"); 5.0
PopulateItemLinkObject = sig.ScanText("48 89 5C 24 08 57 48 83 EC 20 80 7A 06 00 48 8B DA 48 8B F9 74 14 48 8B CA E8 32 03 00 00 48 8B C8 E8 7A 12 AF FF 8B C8 EB 1D 0F B6 42 14 8B 4A");
//PopulateItemLinkObject = sig.ScanText( "48 89 5C 24 08 57 48 83 EC 20 80 7A 06 00 48 8B DA 48 8B F9 74 14 48 8B CA E8 32 03 00 00 48 8B C8 E8 ?? ?? B0 FF 8B C8 EB 1D 0F B6 42 14 8B 4A"); 5.0
PopulateItemLinkObject = sig.ScanText("48 89 5C 24 08 57 48 83 EC 20 80 7A 06 00 48 8B DA 48 8B F9 74 14 48 8B CA E8 32 03 00 00 48 8B C8 E8 ?? ?? ?? FF 8B C8 EB 1D 0F B6 42 14 8B 4A");
}
}
}

View file

@ -35,8 +35,8 @@ namespace Dalamud.Game.Internal.Gui {
this.Address.Setup(scanner);
this.byteBase = scanner.Module.BaseAddress;
this.comboTimer = byteBase + 0x1BB5B50;
this.lastComboMove = byteBase + 0x1BB5B54;
this.comboTimer = byteBase + 0x1BB0B50;
this.lastComboMove = byteBase + 0x1BB0B54;
CustomIDs = new HashSet<uint>();
VanillaIDs = new HashSet<uint>();
@ -99,7 +99,7 @@ namespace Dalamud.Game.Internal.Gui {
// TODO: this is currently broken
// As it stands, don't rely on localCharacter.level for anything.
var localPlayer = this.dalamud.ClientState.LocalPlayer;
//var localPlayer = this.dalamud.ClientState.LocalPlayer;
// Don't clutter the spaghetti any worse than it already is.
var lastMove = Marshal.ReadInt32(this.lastComboMove);

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Runtime.InteropServices;
using Serilog;
@ -6,7 +6,7 @@ namespace Dalamud.Game.Internal.Libc {
public sealed class LibcFunction {
// TODO: prolly callconv is not okay in x86
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate IntPtr StdStringFromCStringDelegate(IntPtr pStdString, [MarshalAs(UnmanagedType.LPUTF8Str)]string content, IntPtr size);
private delegate IntPtr StdStringFromCStringDelegate(IntPtr pStdString, [MarshalAs(UnmanagedType.LPArray)]byte[] content, IntPtr size);
// TODO: prolly callconv is not okay in x86
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
@ -25,7 +25,7 @@ namespace Dalamud.Game.Internal.Libc {
this.stdStringDeallocate = Marshal.GetDelegateForFunctionPointer<StdStringDeallocateDelegate>(Address.StdStringDeallocate);
}
public OwnedStdString NewString(string content) {
public OwnedStdString NewString(byte[] content) {
Log.Verbose("Allocating");
// While 0x70 bytes in the memory should be enough in DX11 version,
@ -36,7 +36,7 @@ namespace Dalamud.Game.Internal.Libc {
var npos = new IntPtr(0xFFFFFFFF); // assumed to be -1 (0xFFFFFFFF in x86, 0xFFFFFFFF_FFFFFFFF in amd64)
var pReallocString = this.stdStringCtorCString(pString, content, npos);
Log.Verbose("Prev: {Prev} Now: {Now}", pString, pReallocString);
//Log.Verbose("Prev: {Prev} Now: {Now}", pString, pReallocString);
return new OwnedStdString(pReallocString, DeallocateStdString);
}

View file

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Dalamud.Hooking;
using Serilog;
@ -11,6 +13,7 @@ namespace Dalamud.Game.Internal.Network {
private readonly Hook<ProcessZonePacketDelegate> processZonePacketHook;
private GameNetworkAddressResolver Address { get; }
private IntPtr baseAddress;
public delegate void OnZonePacketDelegate(IntPtr dataPtr);
@ -18,6 +21,8 @@ namespace Dalamud.Game.Internal.Network {
private readonly Dalamud dalamud;
private readonly Queue<byte[]> zoneInjectQueue = new Queue<byte[]>();
public GameNetwork(Dalamud dalamud, SigScanner scanner) {
this.dalamud = dalamud;
Address = new GameNetworkAddressResolver();
@ -40,6 +45,8 @@ namespace Dalamud.Game.Internal.Network {
}
private void ProcessZonePacketDetour(IntPtr a, IntPtr b, IntPtr dataPtr) {
this.baseAddress = a;
// Call events
this.OnZonePacket?.Invoke(dataPtr);
@ -60,5 +67,47 @@ namespace Dalamud.Game.Internal.Network {
this.processZonePacketHook.Original(a, b, dataPtr);
}
}
private void InjectZoneProtoPacket(byte[] data) {
this.zoneInjectQueue.Enqueue(data);
}
private void InjectActorControl(short cat, int param1) {
var packetData = new byte[] {
0x14, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x17, 0x7C, 0xC5, 0x5D, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x48, 0xB2, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x43, 0x7F, 0x00, 0x00
};
BitConverter.GetBytes((short) cat).CopyTo(packetData, 0x10);
BitConverter.GetBytes((UInt32) param1).CopyTo(packetData, 0x14);
InjectZoneProtoPacket(packetData);
}
public void InjectBgmTest(int key) {
InjectActorControl(0xa1, key);
}
/// <summary>
/// Process a chat queue.
/// </summary>
public void UpdateQueue(Framework framework)
{
while (this.zoneInjectQueue.Count > 0)
{
var packetData = this.zoneInjectQueue.Dequeue();
var unmanagedPacketData = Marshal.AllocHGlobal(packetData.Length);
Marshal.Copy(packetData, 0, unmanagedPacketData, packetData.Length);
if (this.baseAddress != IntPtr.Zero) {
this.processZonePacketHook.Original(this.baseAddress, IntPtr.Zero, unmanagedPacketData);
}
Marshal.FreeHGlobal(unmanagedPacketData);
}
}
}
}

View file

@ -1,5 +1,8 @@
using Dalamud.Game.Network.Structures;
namespace Dalamud.Game.Network.MarketBoardUploaders {
internal interface IMarketBoardUploader {
void Upload(MarketBoardItemRequest itemRequest);
void UploadTax(MarketTaxRates taxRates);
}
}

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Net;
using Dalamud.Game.Network.MarketBoardUploaders;
using Dalamud.Game.Network.MarketBoardUploaders.Universalis;
using Dalamud.Game.Network.Structures;
using Newtonsoft.Json;
using Serilog;
@ -86,5 +87,31 @@ namespace Dalamud.Game.Network.Universalis.MarketBoardUploaders {
Log.Verbose("Universalis data upload for item#{0} completed.", request.CatalogId);
}
}
public void UploadTax(MarketTaxRates taxRates) {
using (var client = new WebClient())
{
var taxRatesRequest = new UniversalisTaxUploadRequest();
taxRatesRequest.WorldId = this.dalamud.ClientState.LocalPlayer.CurrentWorld.Id;
taxRatesRequest.UploaderId = this.dalamud.ClientState.LocalContentId;
taxRatesRequest.TaxData = new UniversalisTaxData {
LimsaLominsa = taxRates.LimsaLominsaTax,
Gridania = taxRates.GridaniaTax,
Uldah = taxRates.UldahTax,
Ishgard = taxRates.IshgardTax,
Kugane = taxRates.KuganeTax,
Crystarium = taxRates.CrystariumTax
};
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
var historyUpload = JsonConvert.SerializeObject(taxRatesRequest);
client.UploadString(ApiBase + $"/upload/{ApiKey}", "POST", historyUpload);
Log.Verbose(historyUpload);
Log.Verbose("Universalis tax upload completed.");
}
}
}
}

View file

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Dalamud.Game.Network.MarketBoardUploaders.Universalis
{
class UniversalisTaxUploadRequest
{
[JsonProperty("uploaderID")]
public ulong UploaderId { get; set; }
[JsonProperty("worldID")]
public int WorldId { get; set; }
[JsonProperty("marketTaxRates")]
public UniversalisTaxData TaxData { get; set; }
}
class UniversalisTaxData {
[JsonProperty("limsaLominsa")]
public uint LimsaLominsa { get; set; }
[JsonProperty("gridania")]
public uint Gridania { get; set; }
[JsonProperty("uldah")]
public uint Uldah { get; set; }
[JsonProperty("ishgard")]
public uint Ishgard { get; set; }
[JsonProperty("kugane")]
public uint Kugane { get; set; }
[JsonProperty("crystarium")]
public uint Crystarium { get; set; }
}
}

View file

@ -29,6 +29,7 @@ namespace Dalamud.Game.Network {
private void OnZonePacket(IntPtr dataPtr) {
var opCode = (ZoneOpCode) Marshal.ReadInt16(dataPtr, 2);
/*
if (opCode == ZoneOpCode.RetainerSaleItemId) {
var itemId = Marshal.ReadInt32(dataPtr + 16);
var amount = Marshal.ReadByte(dataPtr + 32);
@ -58,22 +59,25 @@ namespace Dalamud.Game.Network {
return;
}
*/
if (opCode == ZoneOpCode.CfNotify) {
if (opCode == ZoneOpCode.CfNotifyPop) {
var data = new byte[64];
Marshal.Copy(dataPtr, data, 0, 64);
var notifyType = data[16];
var contentFinderConditionId = BitConverter.ToInt16(data, 28);
var contentFinderConditionId = BitConverter.ToInt16(data, 36);
Task.Run(async () => {
if (notifyType != 2 || contentFinderConditionId == 0)
if (notifyType != 3 || contentFinderConditionId == 0)
return;
var contentFinderCondition =
await XivApi.GetContentFinderCondition(contentFinderConditionId);
this.dalamud.Framework.Gui.Chat.Print($"Duty pop: " + contentFinderCondition["Name"]);
if (this.dalamud.BotManager.IsConnected)
await this.dalamud.BotManager.ProcessCfPop(contentFinderCondition);
});
@ -173,17 +177,35 @@ namespace Dalamud.Game.Network {
Log.Verbose("Added history for item#{0}", listing.CatalogId);
}
if (opCode == ZoneOpCode.MarketTaxRates)
{
var taxes = MarketTaxRates.Read(dataPtr + 0x10);
Log.Verbose("MarketTaxRates: limsa#{0} grid#{1} uldah#{2} ish#{3} kugane#{4} cr#{5}",
taxes.LimsaLominsaTax, taxes.GridaniaTax, taxes.UldahTax, taxes.IshgardTax, taxes.KuganeTax, taxes.CrystariumTax);
try
{
Task.Run(() => this.uploader.UploadTax(taxes));
}
catch (Exception ex)
{
Log.Error(ex, "Market Board data upload failed.");
}
}
}
}
private enum ZoneOpCode {
CfNotifyPop = 0x32D,
CfNotify = 0x8F,
RetainerSaleItemId = 0x13F, // TODO these are probably not accurate
RetainerSaleFinish = 0x138,
FateSpawn = 0x226,
MarketBoardItemRequestStart = 0x39D,
MarketBoardOfferings = 0x36A,
MarketBoardHistory = 0x194
MarketTaxRates = 0x39F,
MarketBoardItemRequestStart = 0xF2,
MarketBoardOfferings = 0x1E2,
MarketBoardHistory = 0x123
}
}
}

View file

@ -3,22 +3,28 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Dalamud.Game.Network.Structures {
public class MarketBoardCurrentOfferings {
namespace Dalamud.Game.Network.Structures
{
public class MarketBoardCurrentOfferings
{
public List<MarketBoardItemListing> ItemListings;
public int ListingIndexEnd;
public int ListingIndexStart;
public int RequestId;
public static unsafe MarketBoardCurrentOfferings Read(IntPtr dataPtr) {
public static unsafe MarketBoardCurrentOfferings Read(IntPtr dataPtr)
{
var output = new MarketBoardCurrentOfferings();
using (var stream = new UnmanagedMemoryStream((byte*) dataPtr.ToPointer(), 1544)) {
using (var reader = new BinaryReader(stream)) {
using (var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544))
{
using (var reader = new BinaryReader(stream))
{
output.ItemListings = new List<MarketBoardItemListing>();
for (var i = 0; i < 10; i++) {
for (var i = 0; i < 10; i++)
{
var listingEntry = new MarketBoardItemListing();
listingEntry.ListingId = reader.ReadUInt64();
@ -38,7 +44,8 @@ namespace Dalamud.Game.Network.Structures {
listingEntry.Materia = new List<MarketBoardItemListing.ItemMateria>();
for (var materiaIndex = 0; materiaIndex < 5; materiaIndex++) {
for (var materiaIndex = 0; materiaIndex < 5; materiaIndex++)
{
var materiaVal = reader.ReadUInt16();
var materiaEntry = new MarketBoardItemListing.ItemMateria();
@ -76,7 +83,8 @@ namespace Dalamud.Game.Network.Structures {
return output;
}
public class MarketBoardItemListing {
public class MarketBoardItemListing
{
public ulong ArtisanId;
public uint CatalogId;
public bool IsHq;
@ -97,7 +105,8 @@ namespace Dalamud.Game.Network.Structures {
public int StainId;
public uint TotalTax;
public class ItemMateria {
public class ItemMateria
{
public int Index;
public int MateriaId;
}

View file

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
namespace Dalamud.Game.Network.Structures
{
public class MarketTaxRates
{
public uint LimsaLominsaTax;
public uint GridaniaTax;
public uint UldahTax;
public uint IshgardTax;
public uint KuganeTax;
public uint CrystariumTax;
public static unsafe MarketTaxRates Read(IntPtr dataPtr)
{
var output = new MarketTaxRates();
using (var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544))
{
using (var reader = new BinaryReader(stream))
{
stream.Position += 8;
output.LimsaLominsaTax = reader.ReadUInt32();
output.GridaniaTax = reader.ReadUInt32();
output.UldahTax = reader.ReadUInt32();
output.IshgardTax = reader.ReadUInt32();
output.KuganeTax = reader.ReadUInt32();
output.CrystariumTax = reader.ReadUInt32();
}
}
return output;
}
}
}

View file

@ -0,0 +1,55 @@
using Dalamud.Hooking;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Dalamud.Game
{
internal sealed class WinSockHandlers : IDisposable
{
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
private delegate IntPtr SocketDelegate(int af, int type, int protocol);
private Hook<SocketDelegate> ws2SocketHook;
[DllImport("ws2_32.dll", CallingConvention = CallingConvention.Winapi)]
private static extern int setsockopt(IntPtr socket, SocketOptionLevel level, SocketOptionName optName, ref IntPtr optVal, int optLen);
public WinSockHandlers() {
this.ws2SocketHook = Hook<SocketDelegate>.FromSymbol("ws2_32.dll", "socket", new SocketDelegate(OnSocket));
this.ws2SocketHook.Enable();
}
private IntPtr OnSocket(int af, int type, int protocol)
{
var socket = this.ws2SocketHook.Original(af, type, protocol);
// IPPROTO_TCP
if (type == 1)
{
// INVALID_SOCKET
if (socket != new IntPtr(-1))
{
// In case you're not aware of it: (albeit you should)
// https://linux.die.net/man/7/tcp
// https://assets.extrahop.com/whitepapers/TCP-Optimization-Guide-by-ExtraHop.pdf
var value = new IntPtr(1);
setsockopt(socket, SocketOptionLevel.Tcp, SocketOptionName.NoDelay, ref value, 4);
// Enable tcp_quickack option. This option is undocumented in MSDN but it is supported in Windows 7 and onwards.
value = new IntPtr(1);
setsockopt(socket, SocketOptionLevel.Tcp, (SocketOptionName)12, ref value, 4);
}
}
return socket;
}
public void Dispose() {
ws2SocketHook.Dispose();
}
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using EasyHook;
@ -9,7 +9,7 @@ namespace Dalamud.Hooking {
/// This class is basically a thin wrapper around the LocalHook type to provide helper functions.
/// </summary>
/// <typeparam name="T">Delegate type to represents a function prototype. This must be the same prototype as original function do.</typeparam>
public sealed class Hook<T> : IDisposable where T : class {
public sealed class Hook<T> : IDisposable where T : Delegate {
private bool isDisposed;
private readonly IntPtr address;