Magic the magic happen

This commit is contained in:
Raymond Lynch 2021-07-11 16:32:29 -04:00
parent 84769ae5b7
commit 658eedca37
188 changed files with 10329 additions and 3549 deletions

View file

@ -24,6 +24,7 @@ csharp_preferred_modifier_order = public, private, protected, internal, new, abs
csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
dotnet_code_quality_unused_parameters = non_public
dotnet_naming_rule.event_rule.severity = warning
dotnet_naming_rule.event_rule.style = on_upper_camel_case_style
dotnet_naming_rule.event_rule.symbols = event_symbols
@ -56,11 +57,13 @@ dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static
dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private
dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field
dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static,readonly
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:suggestion
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:suggestion
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion
dotnet_style_parentheses_in_arithmetic_binary_operators =always_for_clarity:suggestion
dotnet_style_parentheses_in_other_binary_operators =always_for_clarity:suggestion
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
dotnet_style_parentheses_in_other_operators=always_for_clarity:silent
dotnet_style_object_initializer = false
# ReSharper properties
resharper_align_linq_query = true
@ -105,8 +108,10 @@ resharper_redundant_base_qualifier_highlighting = none
resharper_suggest_var_or_type_built_in_types_highlighting = hint
resharper_suggest_var_or_type_elsewhere_highlighting = hint
resharper_suggest_var_or_type_simple_types_highlighting = hint
csharp_style_deconstructed_variable_declaration=true:silent
[*.{appxmanifest,asax,ascx,aspx,axaml,axml,build,c,c++,cc,cginc,compute,config,cp,cpp,cs,cshtml,csproj,css,cu,cuh,cxx,dbml,discomap,dtd,h,hh,hlsl,hlsli,hlslinc,hpp,htm,html,hxx,inc,inl,ino,ipp,js,json,jsproj,jsx,lsproj,master,mpp,mq4,mq5,mqh,njsproj,nuspec,paml,proj,props,proto,razor,resjson,resw,resx,skin,StyleCop,targets,tasks,tpp,ts,tsx,usf,ush,vb,vbproj,xaml,xamlx,xml,xoml,xsd}]
indent_style = space
indent_size = 4
tab_width = 4
dotnet_style_parentheses_in_other_operators=always_for_clarity:silent

View file

@ -12,7 +12,7 @@ jobs:
steps:
- name: Remove old artifacts
uses: c-hive/gha-remove-artifacts@24dc23384a1fa6a058b79c73727ae0cb2200ca4c
uses: c-hive/gha-remove-artifacts@v1.2.0
with:
age: '1 month'
skip-tags: true

View file

@ -7,15 +7,12 @@ jobs:
name: Build on Windows
runs-on: windows-2019
steps:
- uses: actions/checkout@v2
- name: Checkout Dalamud
uses: actions/checkout@v2
with:
submodules: recursive
- name: Setup Nuget
uses: nuget/setup-nuget@v1
with:
nuget-version: latest
- name: Restore Nuget Packages
run: nuget restore Dalamud.sln
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1.0.2
- name: Define VERSION
run: |
$env:COMMIT = $env:GITHUB_SHA.Substring(0, 7)
@ -25,18 +22,17 @@ jobs:
($env:REPO_NAME) >> VERSION
($env:BRANCH) >> VERSION
($env:COMMIT) >> VERSION
- name: Build DotNet4
run: |
cd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\"
.\MSBuild.exe $Env:GITHUB_WORKSPACE\Dalamud.sln /t:Build /p:Configuration=Release /p:DefineConstants=XL_NOAUTOUPDATE
- name: Run xUnit Tests
run: |
${{github.workspace}}\packages\xunit.runner.console.2.4.1\tools\net472\xunit.console.exe ${{github.workspace}}\Dalamud.Test\bin\Release\Dalamud.Test.dll
- name: Build Dalamud
run: .\build.ps1 compile
- name: Test Dalamud
run: .\build.ps1 test
- name: Create hashlist
run: .\CreateHashList.ps1 .\bin\Release
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: dalamud-artifact
path: bin\
path: bin\Release
deploy_stg:
name: Deploy dalamud-distrib staging

3
.gitmodules vendored
View file

@ -4,3 +4,6 @@
[submodule "lib/FFXIVClientStructs"]
path = lib/FFXIVClientStructs
url = https://github.com/goatcorp/FFXIVClientStructs.git
[submodule "lib/SharpDX.Desktop"]
path = lib/SharpDX.Desktop
url = https://github.com/goatcorp/SharpDX.Desktop.git

112
.nuke/build.schema.json Normal file
View file

@ -0,0 +1,112 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Build Schema",
"$ref": "#/definitions/build",
"definitions": {
"build": {
"type": "object",
"properties": {
"Configuration": {
"type": "string",
"description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)",
"enum": [
"Debug",
"Release"
]
},
"Continue": {
"type": "boolean",
"description": "Indicates to continue a previously failed build attempt"
},
"Help": {
"type": "boolean",
"description": "Shows the help text for this build assembly"
},
"Host": {
"type": "string",
"description": "Host for execution. Default is 'automatic'",
"enum": [
"AppVeyor",
"AzurePipelines",
"Bamboo",
"Bitrise",
"GitHubActions",
"GitLab",
"Jenkins",
"SpaceAutomation",
"TeamCity",
"Terminal",
"TravisCI"
]
},
"NoLogo": {
"type": "boolean",
"description": "Disables displaying the NUKE logo"
},
"Plan": {
"type": "boolean",
"description": "Shows the execution plan (HTML)"
},
"Profile": {
"type": "array",
"description": "Defines the profiles to load",
"items": {
"type": "string"
}
},
"Root": {
"type": "string",
"description": "Root directory during build execution"
},
"Skip": {
"type": "array",
"description": "List of targets to be skipped. Empty list skips all dependencies",
"items": {
"type": "string",
"enum": [
"Clean",
"Compile",
"CompileDalamud",
"CompileDalamudBoot",
"CompileInjector",
"CompileInjectorBoot",
"Restore",
"Test"
]
}
},
"Solution": {
"type": "string",
"description": "Path to a solution file that is automatically loaded"
},
"Target": {
"type": "array",
"description": "List of targets to be invoked. Default is '{default_target}'",
"items": {
"type": "string",
"enum": [
"Clean",
"Compile",
"CompileDalamud",
"CompileDalamudBoot",
"CompileInjector",
"CompileInjectorBoot",
"Restore",
"Test"
]
}
},
"Verbosity": {
"type": "string",
"description": "Logging verbosity during build execution. Default is 'Normal'",
"enum": [
"Minimal",
"Normal",
"Quiet",
"Verbose"
]
}
}
}
}
}

4
.nuke/parameters.json Normal file
View file

@ -0,0 +1,4 @@
{
"$schema": "./build.schema.json",
"Solution": "Dalamud.sln"
}

View file

@ -1,7 +1,11 @@
$hashes = @{}
$hashes = [ordered]@{}
Get-ChildItem $args[0] -Exclude dalamud.txt,*.zip,*.pdb,*.ipdb | Foreach-Object {
$hashes.Add($_.Name, (Get-FileHash $_.FullName -Algorithm MD5).Hash)
Set-Location $args[0]
Get-ChildItem -File -Recurse -Exclude dalamud.txt,*.zip,*.pdb,*.ipdb | Foreach-Object {
$key = ($_.FullName | Resolve-Path -Relative).TrimStart(".\\")
$val = (Get-FileHash $_.FullName -Algorithm MD5).Hash
$hashes.Add($key, $val)
}
ConvertTo-Json $hashes | Out-File -FilePath (Join-Path $args[0] "hashes.json")
$hashes | ConvertTo-Json | Out-File -FilePath "hashes.json"

View file

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{55198DC3-A03D-408E-A8EB-2077780C8576}</ProjectGuid>
<RootNamespace>Dalamud_Boot</RootNamespace>
<Configuration Condition=" '$(Configuration)'=='' ">Debug</Configuration>
<Platform>x64</Platform>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<LinkIncremental>false</LinkIncremental>
<CharacterSet>Unicode</CharacterSet>
<OutDir>..\bin\$(Configuration)\</OutDir>
<IntDir>obj\$(Configuration)\</IntDir>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<PreprocessorDefinitions>CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalLibraryDirectories>..\lib\CoreCLR;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>false</IntrinsicFunctions>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>false</EnableCOMDATFolding>
<OptimizeReferences>false</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ItemGroup>
<Content Include="..\lib\CoreCLR\nethost\nethost.dll">
<Link>nethost.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\lib\CoreCLR\boot.cpp" />
<ClCompile Include="..\lib\CoreCLR\CoreCLR.cpp" />
<ClCompile Include="..\lib\CoreCLR\pch.cpp" />
<ClCompile Include="dllmain.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\lib\CoreCLR\boot.h" />
<ClInclude Include="..\lib\CoreCLR\CoreCLR.h" />
<ClInclude Include="..\lib\CoreCLR\core\coreclr_delegates.h" />
<ClInclude Include="..\lib\CoreCLR\core\hostfxr.h" />
<ClInclude Include="..\lib\CoreCLR\framework.h" />
<ClInclude Include="..\lib\CoreCLR\nethost\nethost.h" />
<ClInclude Include="..\lib\CoreCLR\pch.h" />
</ItemGroup>
<ItemGroup>
<Library Include="..\lib\CoreCLR\nethost\libnethost.lib" />
<Library Include="..\lib\CoreCLR\nethost\nethost.lib" />
</ItemGroup>
<Target Name="RemoveExtraFiles" AfterTargets="PostBuildEvent">
<Delete Files="$(OutDir)$(TargetName).lib" />
<Delete Files="$(OutDir)$(TargetName).exp" />
</Target>
</Project>

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Library Files">
<UniqueIdentifier>{18be40ac-9367-46ff-b848-4c528aa97a8d}</UniqueIdentifier>
<Extensions>lib</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\lib\CoreCLR\pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\lib\CoreCLR\boot.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\lib\CoreCLR\CoreCLR.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\lib\CoreCLR\core\coreclr_delegates.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\core\hostfxr.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\nethost\nethost.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\CoreCLR.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\framework.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\boot.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Library Include="..\lib\CoreCLR\nethost\nethost.lib">
<Filter>Library Files</Filter>
</Library>
<Library Include="..\lib\CoreCLR\nethost\libnethost.lib">
<Filter>Library Files</Filter>
</Library>
</ItemGroup>
</Project>

66
Dalamud.Boot/dllmain.cpp Normal file
View file

@ -0,0 +1,66 @@
#define WIN32_LEAN_AND_MEAN
#define DllExport extern "C" __declspec(dllexport)
#include <filesystem>
#include <Windows.h>
#include "..\lib\CoreCLR\CoreCLR.h"
#include "..\lib\CoreCLR\boot.h"
HMODULE g_hModule;
DllExport DWORD WINAPI Initialize(LPVOID lpParam)
{
#if defined(_DEBUG)
ConsoleSetup(L"Dalamud Boot");
#endif
wchar_t _module_path[MAX_PATH];
GetModuleFileNameW(g_hModule, _module_path, sizeof _module_path / 2);
std::filesystem::path fs_module_path(_module_path);
std::wstring runtimeconfig_path = _wcsdup(fs_module_path.replace_filename(L"Dalamud.runtimeconfig.json").c_str());
std::wstring module_path = _wcsdup(fs_module_path.replace_filename(L"Dalamud.dll").c_str());
// =========================================================================== //
void* entrypoint_vfn;
int result = InitializeClrAndGetEntryPoint(
runtimeconfig_path,
module_path,
L"Dalamud.EntryPoint, Dalamud",
L"Initialize",
L"Dalamud.EntryPoint+InitDelegate, Dalamud",
&entrypoint_vfn);
if (result != 0)
return result;
typedef void (CORECLR_DELEGATE_CALLTYPE* custom_component_entry_point_fn)(LPVOID);
custom_component_entry_point_fn entrypoint_fn = reinterpret_cast<custom_component_entry_point_fn>(entrypoint_vfn);
printf("Initializing Dalamud... ");
entrypoint_fn(lpParam);
printf("Done!\n");
// =========================================================================== //
#if defined(_DEBUG)
FreeConsole();
#endif
return 0;
}
BOOL APIENTRY DllMain(const HMODULE hModule, const DWORD dwReason, LPVOID lpReserved) {
DisableThreadLibraryCalls(hModule);
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
g_hModule = hModule;
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

49
Dalamud.Boot/main.cpp Normal file
View file

@ -0,0 +1,49 @@
#define WIN32_LEAN_AND_MEAN
#include <filesystem>
#include <Windows.h>
#include "CoreCLR.h"
#include "boot.h"
int wmain(int argc, char** argv)
{
#if defined(_DEBUG)
ConsoleSetup(L"Dalamud Injector Boot");
#endif
wchar_t _module_path[MAX_PATH];
GetModuleFileNameW(NULL, _module_path, sizeof _module_path / 2);
std::filesystem::path fs_module_path(_module_path);
std::wstring runtimeconfig_path = _wcsdup(fs_module_path.replace_filename(L"Dalamud.Injector.runtimeconfig.json").c_str());
std::wstring module_path = _wcsdup(fs_module_path.replace_filename(L"Dalamud.Injector.dll").c_str());
// =========================================================================== //
void* entrypoint_vfn;
int result = InitializeClrAndGetEntryPoint(
runtimeconfig_path,
module_path,
L"Dalamud.Injector.EntryPoint, Dalamud.Injector",
L"Main",
L"Dalamud.Injector.EntryPoint+MainDelegate, Dalamud.Injector",
&entrypoint_vfn);
if (result != 0)
return result;
typedef void (CORECLR_DELEGATE_CALLTYPE* custom_component_entry_point_fn)(int, char**);
custom_component_entry_point_fn entrypoint_fn = reinterpret_cast<custom_component_entry_point_fn>(entrypoint_vfn);
printf("Running Dalamud Injector... ");
entrypoint_fn(argc, argv);
printf("Done!\n");
// =========================================================================== //
#if defined(_DEBUG)
FreeConsole();
#endif
return 0;
}

View file

@ -0,0 +1,61 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>Dalamud.CorePlugin</AssemblyName>
<TargetFramework>net5.0-windows</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<Platforms>x64;AnyCPU</Platforms>
<LangVersion>9.0</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<NoWarn>IDE0003</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>$(appData)\XIVLauncher\devPlugins\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>$(appData)\XIVLauncher\devPlugins\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Lumina" Version="3.3.0" />
<PackageReference Include="Lumina.Excel" Version="5.50.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.333">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Dalamud\Dalamud.csproj">
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj">
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj">
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj">
<Private>false</Private>
</ProjectReference>
</ItemGroup>
</Project>

View file

@ -0,0 +1,16 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
// General
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Preventing long lines", Scope = "namespaceanddescendants", Target = "~N:Dalamud.CorePlugin")]
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "I like regions", Scope = "namespaceanddescendants", Target = "~N:Dalamud.CorePlugin")]
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "I like regions in elements too", Scope = "namespaceanddescendants", Target = "~N:Dalamud.CorePlugin")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying", Scope = "namespaceanddescendants", Target = "~N:Dalamud.CorePlugin")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud.CorePlugin")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud.CorePlugin")]
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud.CorePlugin")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]

View file

@ -0,0 +1,96 @@
using System;
using System.IO;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
namespace Dalamud.CorePlugin
{
/// <summary>
/// This class is a a plugin testbed for developing new Dalamud features with easy access to Dalamud itself.
/// Be careful to not commit anything extra.
/// </summary>
public sealed class PluginImpl : IDalamudPlugin
{
private readonly WindowSystem windowSystem = new("Dalamud.CorePlugin");
private Localization localizationManager;
/// <inheritdoc/>
public string Name => "Dalamud.CorePlugin";
/// <summary>
/// Gets the plugin interface.
/// </summary>
internal DalamudPluginInterface Interface { get; private set; }
/// <inheritdoc/>
public void Initialize(DalamudPluginInterface pluginInterface)
{
try
{
this.InitLoc();
this.Interface = pluginInterface;
// this.windowSystem.AddWindow(your_window);
this.Interface.UiBuilder.OnBuildUi += this.OnDraw;
this.Interface.UiBuilder.OnOpenConfigUi += this.OnOpenConfigUi;
this.Interface.CommandManager.AddHandler("/di", new(this.OnCommand) { HelpMessage = $"Access the {this.Name} plugin." });
}
catch (Exception ex)
{
PluginLog.Error(ex, "kaboom");
}
}
/// <inheritdoc/>
public void Dispose()
{
this.Interface.CommandManager.RemoveHandler("/di");
this.Interface.UiBuilder.OnBuildUi -= this.OnDraw;
this.windowSystem.RemoveAllWindows();
this.Interface.Dispose();
}
private void InitLoc()
{
// CheapLoc needs to be reinitialized here because it tracks the setup by assembly name. New assembly, new setup.
this.localizationManager = new Localization(Path.Combine(Dalamud.Instance.AssetDirectory.FullName, "UIRes", "loc", "dalamud"), "dalamud_");
if (!string.IsNullOrEmpty(Dalamud.Instance.Configuration.LanguageOverride))
{
this.localizationManager.SetupWithLangCode(Dalamud.Instance.Configuration.LanguageOverride);
}
else
{
this.localizationManager.SetupWithUiCulture();
}
}
private void OnDraw()
{
try
{
this.windowSystem.Draw();
}
catch (Exception ex)
{
PluginLog.Error(ex, "Boom");
}
}
private void OnCommand(string command, string args)
{
// this.window.IsOpen = true;
}
private void OnOpenConfigUi(object sender, EventArgs e)
{
// this.window.IsOpen = true;
}
}
}

View file

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin.Internal;
using Dalamud.Plugin.Internal.Exceptions;
using Dalamud.Plugin.Internal.Types;
using ImGuiNET;
namespace Dalamud.CorePlugin
{
/// <summary>
/// Class responsible for drawing the plugin installer.
/// </summary>
internal class PluginWindow : Window, IDisposable
{
private static readonly ModuleLog Log = new("CorePlugin");
private readonly Dalamud dalamud;
/// <summary>
/// Initializes a new instance of the <see cref="PluginWindow"/> class.
/// </summary>
/// <param name="dalamud">The Dalamud instance.</param>
public PluginWindow(Dalamud dalamud)
: base("CorePlugin", ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoScrollbar)
{
this.dalamud = dalamud;
this.IsOpen = true;
this.Size = new Vector2(810, 520);
this.SizeCondition = ImGuiCond.Always;
}
/// <inheritdoc/>
public void Dispose()
{
}
/// <inheritdoc/>
public override void OnOpen()
{
}
/// <inheritdoc/>
public override void Draw()
{
}
}
}

View file

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{8874326B-E755-4D13-90B4-59AB263A3E6B}</ProjectGuid>
<RootNamespace>Dalamud_Injector_Boot</RootNamespace>
<Configuration Condition=" '$(Configuration)'=='' ">Debug</Configuration>
<Platform>x64</Platform>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<TargetName>Dalamud.Injector</TargetName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<LinkIncremental>false</LinkIncremental>
<CharacterSet>Unicode</CharacterSet>
<OutDir>..\bin\$(Configuration)\</OutDir>
<IntDir>obj\$(Configuration)\</IntDir>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<PreprocessorDefinitions>CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalLibraryDirectories>..\lib\CoreCLR;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ProgramDatabaseFile>$(OutDir)$(TargetName).Boot.pdb</ProgramDatabaseFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>false</IntrinsicFunctions>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>false</EnableCOMDATFolding>
<OptimizeReferences>false</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ItemGroup>
<Content Include="..\lib\CoreCLR\nethost\nethost.dll">
<Link>nethost.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Image Include="dalamud.ico" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="resources.rc" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\lib\CoreCLR\boot.cpp" />
<ClCompile Include="..\lib\CoreCLR\CoreCLR.cpp" />
<ClCompile Include="..\lib\CoreCLR\pch.cpp" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\lib\CoreCLR\boot.h" />
<ClInclude Include="..\lib\CoreCLR\CoreCLR.h" />
<ClInclude Include="..\lib\CoreCLR\core\coreclr_delegates.h" />
<ClInclude Include="..\lib\CoreCLR\core\hostfxr.h" />
<ClInclude Include="..\lib\CoreCLR\framework.h" />
<ClInclude Include="..\lib\CoreCLR\nethost\nethost.h" />
<ClInclude Include="..\lib\CoreCLR\pch.h" />
</ItemGroup>
<ItemGroup>
<Library Include="..\lib\CoreCLR\nethost\libnethost.lib" />
<Library Include="..\lib\CoreCLR\nethost\nethost.lib" />
</ItemGroup>
<Target Name="RemoveExtraFiles" AfterTargets="PostBuildEvent">
<Delete Files="$(OutDir)$(TargetName).lib" />
<Delete Files="$(OutDir)$(TargetName).exp" />
</Target>
</Project>

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{4faac519-3a73-4b2b-96e7-fb597f02c0be}</UniqueIdentifier>
<Extensions>ico;rc</Extensions>
</Filter>
<Filter Include="Library Files">
<UniqueIdentifier>{6aff1bed-6979-4bc9-94e8-ddafb626e6bf}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Image Include="dalamud.ico">
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="resources.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\lib\CoreCLR\pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\lib\CoreCLR\boot.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\lib\CoreCLR\CoreCLR.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\lib\CoreCLR\framework.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\boot.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\CoreCLR.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\nethost\nethost.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\core\hostfxr.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\core\coreclr_delegates.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Library Include="..\lib\CoreCLR\nethost\nethost.lib">
<Filter>Library Files</Filter>
</Library>
<Library Include="..\lib\CoreCLR\nethost\libnethost.lib">
<Filter>Library Files</Filter>
</Library>
</ItemGroup>
</Project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Before After
Before After

View file

@ -0,0 +1,49 @@
#define WIN32_LEAN_AND_MEAN
#include <filesystem>
#include <Windows.h>
#include "..\lib\CoreCLR\CoreCLR.h"
#include "..\lib\CoreCLR\boot.h"
int wmain(int argc, char** argv)
{
#if defined(_DEBUG)
ConsoleSetup(L"Dalamud Injector Boot");
#endif
wchar_t _module_path[MAX_PATH];
GetModuleFileNameW(NULL, _module_path, sizeof _module_path / 2);
std::filesystem::path fs_module_path(_module_path);
std::wstring runtimeconfig_path = _wcsdup(fs_module_path.replace_filename(L"Dalamud.Injector.runtimeconfig.json").c_str());
std::wstring module_path = _wcsdup(fs_module_path.replace_filename(L"Dalamud.Injector.dll").c_str());
// =========================================================================== //
void* entrypoint_vfn;
int result = InitializeClrAndGetEntryPoint(
runtimeconfig_path,
module_path,
L"Dalamud.Injector.EntryPoint, Dalamud.Injector",
L"Main",
L"Dalamud.Injector.EntryPoint+MainDelegate, Dalamud.Injector",
&entrypoint_vfn);
if (result != 0)
return result;
typedef void (CORECLR_DELEGATE_CALLTYPE* custom_component_entry_point_fn)(int, char**);
custom_component_entry_point_fn entrypoint_fn = reinterpret_cast<custom_component_entry_point_fn>(entrypoint_vfn);
printf("Running Dalamud Injector... ");
entrypoint_fn(argc, argv);
printf("Done!\n");
// =========================================================================== //
#if defined(_DEBUG)
FreeConsole();
#endif
return 0;
}

View file

@ -0,0 +1 @@
MAINICON ICON "dalamud.ico"

View file

@ -1,60 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Target">
<PlatformTarget>AnyCPU</PlatformTarget>
<TargetFramework>net48</TargetFramework>
<LangVersion>8.0</LangVersion>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<PropertyGroup Label="Build">
<OutputType>WinExe</OutputType>
<OutputPath>$(SolutionDir)bin</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<DebugSymbols>true</DebugSymbols>
<DebugType>Portable</DebugType>
<NoWarn>IDE1006;CS1701;CS1702</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<DocumentationFile>$(SolutionDir)\bin\Dalamud.Injector.xml</DocumentationFile>
<TargetFramework>net5.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PlatformTarget>x64</PlatformTarget>
<Platforms>x64;AnyCPU</Platforms>
<LangVersion>9.0</LangVersion>
</PropertyGroup>
<PropertyGroup Label="Feature">
<InjectorVersion>5.2.4.6</InjectorVersion>
<Description>XIV Launcher addon injector</Description>
<AssemblyVersion>$(InjectorVersion)</AssemblyVersion>
<FileVersion>$(InjectorVersion)</FileVersion>
<Version>$(InjectorVersion)</Version>
</PropertyGroup>
<PropertyGroup Label="Output">
<OutputType>Library</OutputType>
<OutputPath>..\bin\$(Configuration)\</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
</PropertyGroup>
<PropertyGroup Label="Documentation">
<DocumentationFile></DocumentationFile>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Label="Build">
<EnableDynamicLoading>true</EnableDynamicLoading>
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Deterministic>true</Deterministic>
<Nullable>annotations</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AssemblyVersion>5.2.4.6</AssemblyVersion>
<FileVersion>5.2.4.6</FileVersion>
<Description>XIVLauncher addon injection</Description>
<Version>5.2.4.6</Version>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<AppOutputBase>$(MSBuildProjectDirectory)\</AppOutputBase>
<PathMap>$(AppOutputBase)=C:\goatsoft\companysecrets\injector\</PathMap>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageIcon></PackageIcon>
<PackageIconUrl />
<ApplicationIcon>dalamud.ico</ApplicationIcon>
<PropertyGroup Label="Warnings">
<NoWarn>IDE1006;CS1591;CS1701;CS1702</NoWarn>
<!-- IDE1006 - Naming violation -->
<!-- CS1591 - Missing XML comment for publicly visible type or member -->
<!-- CS1701 - Runtime policy may be needed -->
<!-- CS1702 - Runtime policy may be needed -->
</PropertyGroup>
<ItemGroup>
<None Remove="stylecop.json" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="stylecop.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="EasyHook" Version="2.7.6270" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="Iced" Version="1.12.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="PeNet" Version="2.6.3" />
<PackageReference Include="Reloaded.Memory" Version="4.1.1" />
<PackageReference Include="Reloaded.Memory.Buffers" Version="1.3.5" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.333">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DalamudDebugStub\DalamudDebugStub.vcxproj" />
<ProjectReference Include="..\Dalamud\Dalamud.csproj" />
<AdditionalFiles Include="..\stylecop.json" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Windows.Forms" />
<!-- This prevents us from having to include Dalamud itself as a dependency -->
<!-- If the files move just update the paths here -->
<Compile Include="..\Dalamud\ClientLanguage.cs" />
<Compile Include="..\Dalamud\DalamudStartInfo.cs" />
<Compile Include="..\Dalamud\Game\GameVersion.cs" />
<Compile Include="..\Dalamud\Game\GameVersionConverter.cs" />
<Compile Include="..\Dalamud\Interface\Internal\SerilogEventSink.cs" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)'=='Release'">
<Exec Command="powershell -ExecutionPolicy Unrestricted $(SolutionDir)CreateHashList.ps1 $(OutputPath)" />
</Target>
</Project>

View file

@ -0,0 +1,275 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Dalamud.Game;
using Dalamud.Interface.Internal;
using Newtonsoft.Json;
using Reloaded.Memory.Buffers;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using static Dalamud.Injector.NativeFunctions;
namespace Dalamud.Injector
{
/// <summary>
/// Entrypoint to the program.
/// </summary>
public sealed class EntryPoint
{
/// <summary>
/// A delegate used during initialization of the CLR from Dalamud.Injector.Boot.
/// </summary>
/// <param name="argc">Count of arguments.</param>
/// <param name="argvPtr">char** string arguments.</param>
public delegate void MainDelegate(int argc, IntPtr argvPtr);
/// <summary>
/// Start the Dalamud injector.
/// </summary>
/// <param name="argc">Count of arguments.</param>
/// <param name="argvPtr">byte** string arguments.</param>
public static void Main(int argc, IntPtr argvPtr)
{
InitUnhandledException();
InitLogging();
var args = new string[argc];
unsafe
{
var argv = (IntPtr*)argvPtr;
for (var i = 0; i < argc; i++)
{
args[i] = Marshal.PtrToStringUni(argv[i]);
}
}
var cwd = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory;
if (cwd.FullName != Directory.GetCurrentDirectory())
{
Log.Debug($"Changing cwd to {cwd}");
Directory.SetCurrentDirectory(cwd.FullName);
}
var process = GetProcess(args.ElementAtOrDefault(1));
var startInfo = GetStartInfo(args.ElementAtOrDefault(2), process);
startInfo.WorkingDirectory = Directory.GetCurrentDirectory();
// This seems to help with the STATUS_INTERNAL_ERROR condition
Thread.Sleep(1000);
Inject(process, startInfo);
Thread.Sleep(1000);
}
private static void InitUnhandledException()
{
AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) =>
{
if (Log.Logger == null)
{
Console.WriteLine($"A fatal error has occurred: {eventArgs.ExceptionObject}");
}
else
{
var exObj = eventArgs.ExceptionObject;
if (exObj is Exception ex)
{
Log.Error(ex, "A fatal error has occurred.");
}
else
{
Log.Error($"A fatal error has occurred: {eventArgs.ExceptionObject}");
}
}
#if DEBUG
var caption = "Debug Error";
var message =
$"Couldn't inject.\nMake sure that Dalamud was not injected into your target process " +
$"as a release build before and that the target process can be accessed with VM_WRITE permissions.\n\n" +
$"{eventArgs.ExceptionObject}";
#else
var caption = "XIVLauncher Error";
var message =
"Failed to inject the XIVLauncher in-game addon.\nPlease try restarting your game and your PC.\n" +
"If this keeps happening, please report this error.";
#endif
_ = MessageBoxW(IntPtr.Zero, message, caption, MessageBoxType.IconError | MessageBoxType.Ok);
Environment.Exit(0);
};
}
private static void InitLogging()
{
var baseDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
#if DEBUG
var logPath = Path.Combine(baseDirectory, "injector.log");
#else
var logPath = Path.Combine(baseDirectory, "..", "..", "..", "dalamud.injector.log");
#endif
var levelSwitch = new LoggingLevelSwitch();
#if DEBUG
levelSwitch.MinimumLevel = LogEventLevel.Verbose;
#else
levelSwitch.MinimumLevel = LogEventLevel.Information;
#endif
Log.Logger = new LoggerConfiguration()
.WriteTo.Async(a => a.File(logPath))
.WriteTo.Sink(SerilogEventSink.Instance)
.MinimumLevel.ControlledBy(levelSwitch)
.CreateLogger();
}
private static Process GetProcess(string arg)
{
Process process;
var pid = -1;
if (arg != default)
{
pid = int.Parse(arg);
}
switch (pid)
{
case -1:
process = Process.GetProcessesByName("ffxiv_dx11").FirstOrDefault();
if (process == default)
{
throw new Exception("Could not find process");
}
break;
case -2:
var exePath = "C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\ffxiv_dx11.exe";
var exeArgs = new StringBuilder()
.Append("DEV.TestSID=0 DEV.UseSqPack=1 DEV.DataPathType=1 ")
.Append("DEV.LobbyHost01=127.0.0.1 DEV.LobbyPort01=54994 ")
.Append("DEV.LobbyHost02=127.0.0.1 DEV.LobbyPort02=54994 ")
.Append("DEV.LobbyHost03=127.0.0.1 DEV.LobbyPort03=54994 ")
.Append("DEV.LobbyHost04=127.0.0.1 DEV.LobbyPort04=54994 ")
.Append("DEV.LobbyHost05=127.0.0.1 DEV.LobbyPort05=54994 ")
.Append("DEV.LobbyHost06=127.0.0.1 DEV.LobbyPort06=54994 ")
.Append("DEV.LobbyHost07=127.0.0.1 DEV.LobbyPort07=54994 ")
.Append("DEV.LobbyHost08=127.0.0.1 DEV.LobbyPort08=54994 ")
.Append("SYS.Region=0 language=1 version=1.0.0.0 ")
.Append("DEV.MaxEntitledExpansionID=2 DEV.GMServerHost=127.0.0.1 DEV.GameQuitMessageBox=0").ToString();
process = Process.Start(exePath, exeArgs);
Thread.Sleep(1000);
break;
default:
process = Process.GetProcessById(pid);
break;
}
return process;
}
private static DalamudStartInfo GetStartInfo(string arg, Process process)
{
DalamudStartInfo startInfo;
if (arg != default)
{
startInfo = JsonConvert.DeserializeObject<DalamudStartInfo>(Encoding.UTF8.GetString(Convert.FromBase64String(arg)));
}
else
{
var ffxivDir = Path.GetDirectoryName(process.MainModule.FileName);
var appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var xivlauncherDir = Path.Combine(appDataDir, "XIVLauncher");
var gameVerStr = File.ReadAllText(Path.Combine(ffxivDir, "ffxivgame.ver"));
var gameVer = GameVersion.Parse(gameVerStr);
startInfo = new DalamudStartInfo
{
WorkingDirectory = null,
ConfigurationPath = Path.Combine(xivlauncherDir, "dalamudConfig.json"),
PluginDirectory = Path.Combine(xivlauncherDir, "installedPlugins"),
DefaultPluginDirectory = Path.Combine(xivlauncherDir, "devPlugins"),
AssetDirectory = Path.Combine(xivlauncherDir, "dalamudAssets"),
GameVersion = gameVer,
Language = ClientLanguage.English,
OptOutMbCollection = false,
};
Log.Debug(
"Creating a new StartInfo with:\n" +
$" WorkingDirectory: {startInfo.WorkingDirectory}\n" +
$" ConfigurationPath: {startInfo.ConfigurationPath}\n" +
$" PluginDirectory: {startInfo.PluginDirectory}\n" +
$" DefaultPluginDirectory: {startInfo.DefaultPluginDirectory}\n" +
$" AssetDirectory: {startInfo.AssetDirectory}\n" +
$" GameVersion: {startInfo.GameVersion}\n" +
$" Language: {startInfo.Language}\n" +
$" OptOutMbCollection: {startInfo.OptOutMbCollection}");
Log.Information("A Dalamud start info was not found in the program arguments. One has been generated for you.");
Log.Information("Copy the following contents into the program arguments:");
var startInfoJson = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(startInfo)));
Log.Information(startInfoJson);
}
return startInfo;
}
private static void Inject(Process process, DalamudStartInfo startInfo)
{
var nethostName = "nethost.dll";
var bootName = "Dalamud.Boot.dll";
var nethostPath = Path.GetFullPath(nethostName);
var bootPath = Path.GetFullPath(bootName);
// ======================================================
using var injector = new Injector(process);
injector.LoadLibrary(nethostPath, out _);
injector.LoadLibrary(bootPath, out var bootModule);
// ======================================================
var startInfoJson = JsonConvert.SerializeObject(startInfo);
var startInfoBytes = Encoding.UTF8.GetBytes(startInfoJson);
using var startInfoBuffer = new MemoryBufferHelper(process).CreatePrivateMemoryBuffer(startInfoBytes.Length + 0x8);
var startInfoAddress = startInfoBuffer.Add(startInfoBytes);
if (startInfoAddress == IntPtr.Zero)
throw new Exception("Unable to allocate start info JSON");
injector.GetFunctionAddress(bootModule, "Initialize", out var initAddress);
injector.CallRemoteFunction(initAddress, startInfoAddress, out var exitCode);
// ======================================================
if (exitCode > 0)
{
Log.Error($"Dalamud.Boot::Initialize returned {exitCode}");
return;
}
Log.Information("Done");
}
}
}

View file

@ -6,14 +6,8 @@
using System.Diagnostics.CodeAnalysis;
// General
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Preventing long lines", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "I like regions", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "I like regions in elements too", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
// Program.cs
[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used during #if DEBUG", Scope = "member", Target = "~M:Dalamud.Injector.Program.NativeInject(System.Diagnostics.Process)")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "I'll make what I want static", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]

View file

@ -0,0 +1,303 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Iced.Intel;
using PeNet;
using PeNet.Header.Pe;
using Reloaded.Memory.Buffers;
using Reloaded.Memory.Sources;
using Reloaded.Memory.Utilities;
using static Dalamud.Injector.NativeFunctions;
using static Iced.Intel.AssemblerRegisters;
namespace Dalamud.Injector
{
/// <summary>
/// This class implements injecting into a remote process. It is a highly stripped down version of the
/// https://github.com/Reloaded-Project injector/assembler implementation due to issues with Lutris and
/// Wine.
/// </summary>
internal sealed class Injector : IDisposable
{
private readonly Process targetProcess;
private readonly ExternalMemory extMemory;
private readonly CircularBuffer circularBuffer;
private readonly PrivateMemoryBuffer privateBuffer;
private IntPtr loadLibraryShellPtr;
private IntPtr loadLibraryRetPtr;
private IntPtr getProcAddressShellPtr;
private IntPtr getProcAddressRetPtr;
/// <summary>
/// Initializes a new instance of the <see cref="Injector"/> class.
/// </summary>
/// <param name="targetProcess">Process to inject.</param>
public Injector(Process targetProcess)
{
this.targetProcess = targetProcess;
this.extMemory = new ExternalMemory(targetProcess);
this.circularBuffer = new CircularBuffer(4096, this.extMemory);
this.privateBuffer = new MemoryBufferHelper(targetProcess).CreatePrivateMemoryBuffer(4096);
using var kernel32Module = this.GetProcessModule("KERNEL32.DLL");
var kernel32PeFile = new PeFile(kernel32Module.FileName);
var kernel32Exports = kernel32PeFile.ExportedFunctions;
this.SetupLoadLibrary(kernel32Module, kernel32Exports);
this.SetupGetProcAddress(kernel32Module, kernel32Exports);
}
/// <summary>
/// Finalizes an instance of the <see cref="Injector"/> class.
/// </summary>
~Injector() => this.Dispose();
/// <inheritdoc/>
public void Dispose()
{
GC.SuppressFinalize(this);
this.targetProcess?.Dispose();
this.circularBuffer?.Dispose();
this.privateBuffer?.Dispose();
}
/// <summary>
/// Load a module by absolute file path.
/// </summary>
/// <param name="modulePath">Absolute file path.</param>
/// <param name="address">Address to the module.</param>
public void LoadLibrary(string modulePath, out IntPtr address)
{
var lpParameter = this.WriteNullTerminatedUnicodeString(modulePath);
if (lpParameter == IntPtr.Zero)
throw new Exception("Unable to allocate LoadLibraryW parameter");
var threadHandle = CreateRemoteThread(
this.targetProcess.Handle,
IntPtr.Zero,
UIntPtr.Zero,
this.loadLibraryShellPtr,
lpParameter,
CreateThreadFlags.RunImmediately,
out _);
_ = WaitForSingleObject(threadHandle, uint.MaxValue);
this.extMemory.Read(this.loadLibraryRetPtr, out address);
if (address == IntPtr.Zero)
throw new Exception($"Error calling LoadLibraryW with {modulePath}");
}
/// <summary>
/// Get the address of an exported module function.
/// </summary>
/// <param name="module">Module address.</param>
/// <param name="functionName">Name of the exported method.</param>
/// <param name="address">Address to the function.</param>
public void GetFunctionAddress(IntPtr module, string functionName, out IntPtr address)
{
var getProcAddressParams = new GetProcAddressParams(module, this.WriteNullTerminatedASCIIString(functionName));
var lpParameter = this.circularBuffer.Add(ref getProcAddressParams);
if (lpParameter == IntPtr.Zero)
throw new Exception("Unable to allocate GetProcAddress parameter ptr");
var threadHandle = CreateRemoteThread(
this.targetProcess.Handle,
IntPtr.Zero,
UIntPtr.Zero,
this.getProcAddressShellPtr,
lpParameter,
CreateThreadFlags.RunImmediately,
out _);
_ = WaitForSingleObject(threadHandle, uint.MaxValue);
this.extMemory.Read(this.getProcAddressRetPtr, out address);
if (address == IntPtr.Zero)
throw new Exception($"Error calling GetProcAddress with {functionName}");
}
/// <summary>
/// Call a method in a remote process via CreateRemoteThread.
/// </summary>
/// <param name="methodAddress">Method address.</param>
/// <param name="parameterAddress">Parameter address.</param>
/// <param name="exitCode">Thread exit code.</param>
public void CallRemoteFunction(IntPtr methodAddress, IntPtr parameterAddress, out uint exitCode)
{
// Create and initialize a thread at our address and parameter address.
var threadHandle = CreateRemoteThread(
this.targetProcess.Handle,
IntPtr.Zero,
UIntPtr.Zero,
methodAddress,
parameterAddress,
CreateThreadFlags.RunImmediately,
out _);
_ = WaitForSingleObject(threadHandle, uint.MaxValue);
GetExitCodeThread(threadHandle, out exitCode);
}
private void SetupLoadLibrary(ProcessModule kernel32Module, ExportFunction[] kernel32Exports)
{
var offset = this.GetExportedFunctionOffset(kernel32Exports, "LoadLibraryW");
var functionAddr = kernel32Module.BaseAddress + (int)offset;
var functionPtr = this.privateBuffer.Add(ref functionAddr);
if (functionPtr == IntPtr.Zero)
throw new Exception("Unable to allocate LoadLibraryW function ptr");
var dummy = 0L;
this.loadLibraryRetPtr = this.privateBuffer.Add(ref dummy);
if (this.loadLibraryRetPtr == IntPtr.Zero)
throw new Exception("Unable to allocate LoadLibraryW return value");
var func = functionPtr.ToInt64();
var retVal = this.loadLibraryRetPtr.ToInt64();
var asm = new Assembler(64);
asm.sub(rsp, 40); // sub rsp, 40 // Re-align stack to 16 byte boundary + shadow space.
asm.call(__qword_ptr[__qword_ptr[func]]); // call qword [qword func] // CreateRemoteThread lpParameter with string already in ECX.
asm.mov(__qword_ptr[__qword_ptr[retVal]], rax); // mov qword [qword retVal], rax //
asm.add(rsp, 40); // add rsp, 40 // Re-align stack to 16 byte boundary + shadow space.
asm.ret(); // ret // Restore stack ptr. (Callee cleanup)
var bytes = this.Assemble(asm);
this.loadLibraryShellPtr = this.privateBuffer.Add(bytes);
if (this.loadLibraryShellPtr == IntPtr.Zero)
throw new Exception("Unable to allocate LoadLibraryW shellcode");
}
private void SetupGetProcAddress(ProcessModule kernel32Module, ExportFunction[] kernel32Exports)
{
var offset = this.GetExportedFunctionOffset(kernel32Exports, "GetProcAddress");
var functionAddr = kernel32Module.BaseAddress + (int)offset;
var functionPtr = this.privateBuffer.Add(ref functionAddr);
if (functionPtr == IntPtr.Zero)
throw new Exception("Unable to allocate GetProcAddress function ptr");
var dummy = 0L;
this.getProcAddressRetPtr = this.privateBuffer.Add(ref dummy);
if (this.getProcAddressRetPtr == IntPtr.Zero)
throw new Exception("Unable to allocate GetProcAddress return value");
var func = functionPtr.ToInt64();
var retVal = this.getProcAddressRetPtr.ToInt64();
var asm = new Assembler(64);
asm.sub(rsp, 40); // sub rsp, 40 // Re-align stack to 16 byte boundary +32 shadow space
asm.mov(rdx, __qword_ptr[__qword_ptr[rcx + 8]]); // mov rdx, qword [qword rcx + 8] // lpProcName
asm.mov(rcx, __qword_ptr[__qword_ptr[rcx + 0]]); // mov rcx, qword [qword rcx + 0] // hModule
asm.call(__qword_ptr[__qword_ptr[func]]); // call qword [qword func] //
asm.mov(__qword_ptr[__qword_ptr[retVal]], rax); // mov qword [qword retVal] //
asm.add(rsp, 40); // add rsp, 40 // Re-align stack to 16 byte boundary + shadow space.
asm.ret(); // ret // Restore stack ptr. (Callee cleanup)
var bytes = this.Assemble(asm);
this.getProcAddressShellPtr = this.privateBuffer.Add(bytes);
if (this.getProcAddressShellPtr == IntPtr.Zero)
throw new Exception("Unable to allocate GetProcAddress shellcode");
}
private byte[] Assemble(Assembler assembler)
{
using var stream = new MemoryStream();
assembler.Assemble(new StreamCodeWriter(stream), 0);
stream.Position = 0;
var reader = new StreamCodeReader(stream);
int next;
var bytes = new byte[stream.Length];
while ((next = reader.ReadByte()) >= 0)
{
bytes[stream.Position - 1] = (byte)next;
}
return bytes;
}
private ProcessModule GetProcessModule(string moduleName)
{
var modules = this.targetProcess.Modules;
for (var i = 0; i < modules.Count; i++)
{
var module = modules[i];
if (module.ModuleName.Equals(moduleName, StringComparison.InvariantCultureIgnoreCase))
{
return module;
}
}
throw new Exception($"Failed to find {moduleName} in target process' modules");
}
private uint GetExportedFunctionOffset(ExportFunction[] exportFunctions, string functionName)
{
var exportFunction = exportFunctions.FirstOrDefault(func => func.Name == functionName);
if (exportFunction == default)
throw new Exception($"Failed to find exported function {functionName} in target module's exports");
return exportFunction.Address;
}
private IntPtr WriteNullTerminatedASCIIString(string libraryPath)
{
var libraryNameBytes = Encoding.ASCII.GetBytes(libraryPath + '\0');
var value = this.circularBuffer.Add(libraryNameBytes);
if (value == IntPtr.Zero)
throw new Exception("Unable to write ASCII string to buffer");
return value;
}
private IntPtr WriteNullTerminatedUnicodeString(string libraryPath)
{
var libraryNameBytes = Encoding.Unicode.GetBytes(libraryPath + '\0');
var value = this.circularBuffer.Add(libraryNameBytes);
if (value == IntPtr.Zero)
throw new Exception("Unable to write Unicode string to buffer");
return value;
}
[StructLayout(LayoutKind.Sequential)]
private struct GetProcAddressParams
{
public GetProcAddressParams(IntPtr hModule, IntPtr lPProcName)
{
this.HModule = hModule.ToInt64();
this.LPProcName = lPProcName.ToInt64();
}
public long HModule { get; set; }
public long LPProcName { get; set; }
}
}
}

View file

@ -1,14 +1,234 @@
using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
namespace Dalamud.Injector
{
/// <summary>
/// Native functions.
/// Native user32 functions.
/// </summary>
internal static class NativeFunctions
internal static partial class NativeFunctions
{
/// <summary>
/// MB_* from winuser.
/// </summary>
public enum MessageBoxType : uint
{
/// <summary>
/// The default value for any of the various subtypes.
/// </summary>
DefaultValue = 0x0,
// To indicate the buttons displayed in the message box, specify one of the following values.
/// <summary>
/// The message box contains three push buttons: Abort, Retry, and Ignore.
/// </summary>
AbortRetryIgnore = 0x2,
/// <summary>
/// The message box contains three push buttons: Cancel, Try Again, Continue. Use this message box type instead
/// of MB_ABORTRETRYIGNORE.
/// </summary>
CancelTryContinue = 0x6,
/// <summary>
/// Adds a Help button to the message box. When the user clicks the Help button or presses F1, the system sends
/// a WM_HELP message to the owner.
/// </summary>
Help = 0x4000,
/// <summary>
/// The message box contains one push button: OK. This is the default.
/// </summary>
Ok = DefaultValue,
/// <summary>
/// The message box contains two push buttons: OK and Cancel.
/// </summary>
OkCancel = 0x1,
/// <summary>
/// The message box contains two push buttons: Retry and Cancel.
/// </summary>
RetryCancel = 0x5,
/// <summary>
/// The message box contains two push buttons: Yes and No.
/// </summary>
YesNo = 0x4,
/// <summary>
/// The message box contains three push buttons: Yes, No, and Cancel.
/// </summary>
YesNoCancel = 0x3,
// To display an icon in the message box, specify one of the following values.
/// <summary>
/// An exclamation-point icon appears in the message box.
/// </summary>
IconExclamation = 0x30,
/// <summary>
/// An exclamation-point icon appears in the message box.
/// </summary>
IconWarning = IconExclamation,
/// <summary>
/// An icon consisting of a lowercase letter i in a circle appears in the message box.
/// </summary>
IconInformation = 0x40,
/// <summary>
/// An icon consisting of a lowercase letter i in a circle appears in the message box.
/// </summary>
IconAsterisk = IconInformation,
/// <summary>
/// A question-mark icon appears in the message box.
/// The question-mark message icon is no longer recommended because it does not clearly represent a specific type
/// of message and because the phrasing of a message as a question could apply to any message type. In addition,
/// users can confuse the message symbol question mark with Help information. Therefore, do not use this question
/// mark message symbol in your message boxes. The system continues to support its inclusion only for backward
/// compatibility.
/// </summary>
IconQuestion = 0x20,
/// <summary>
/// A stop-sign icon appears in the message box.
/// </summary>
IconStop = 0x10,
/// <summary>
/// A stop-sign icon appears in the message box.
/// </summary>
IconError = IconStop,
/// <summary>
/// A stop-sign icon appears in the message box.
/// </summary>
IconHand = IconStop,
// To indicate the default button, specify one of the following values.
/// <summary>
/// The first button is the default button.
/// MB_DEFBUTTON1 is the default unless MB_DEFBUTTON2, MB_DEFBUTTON3, or MB_DEFBUTTON4 is specified.
/// </summary>
DefButton1 = DefaultValue,
/// <summary>
/// The second button is the default button.
/// </summary>
DefButton2 = 0x100,
/// <summary>
/// The third button is the default button.
/// </summary>
DefButton3 = 0x200,
/// <summary>
/// The fourth button is the default button.
/// </summary>
DefButton4 = 0x300,
// To indicate the modality of the dialog box, specify one of the following values.
/// <summary>
/// The user must respond to the message box before continuing work in the window identified by the hWnd parameter.
/// However, the user can move to the windows of other threads and work in those windows. Depending on the hierarchy
/// of windows in the application, the user may be able to move to other windows within the thread. All child windows
/// of the parent of the message box are automatically disabled, but pop-up windows are not. MB_APPLMODAL is the
/// default if neither MB_SYSTEMMODAL nor MB_TASKMODAL is specified.
/// </summary>
ApplModal = DefaultValue,
/// <summary>
/// Same as MB_APPLMODAL except that the message box has the WS_EX_TOPMOST style.
/// Use system-modal message boxes to notify the user of serious, potentially damaging errors that require immediate
/// attention (for example, running out of memory). This flag has no effect on the user's ability to interact with
/// windows other than those associated with hWnd.
/// </summary>
SystemModal = 0x1000,
/// <summary>
/// Same as MB_APPLMODAL except that all the top-level windows belonging to the current thread are disabled if the
/// hWnd parameter is NULL. Use this flag when the calling application or library does not have a window handle
/// available but still needs to prevent input to other windows in the calling thread without suspending other threads.
/// </summary>
TaskModal = 0x2000,
// To specify other options, use one or more of the following values.
/// <summary>
/// Same as desktop of the interactive window station. For more information, see Window Stations. If the current
/// input desktop is not the default desktop, MessageBox does not return until the user switches to the default
/// desktop.
/// </summary>
DefaultDesktopOnly = 0x20000,
/// <summary>
/// The text is right-justified.
/// </summary>
Right = 0x80000,
/// <summary>
/// Displays message and caption text using right-to-left reading order on Hebrew and Arabic systems.
/// </summary>
RtlReading = 0x100000,
/// <summary>
/// The message box becomes the foreground window. Internally, the system calls the SetForegroundWindow function
/// for the message box.
/// </summary>
SetForeground = 0x10000,
/// <summary>
/// The message box is created with the WS_EX_TOPMOST window style.
/// </summary>
Topmost = 0x40000,
/// <summary>
/// The caller is a service notifying the user of an event. The function displays a message box on the current active
/// desktop, even if there is no user logged on to the computer.
/// </summary>
ServiceNotification = 0x200000,
}
/// <summary>
/// Displays a modal dialog box that contains a system icon, a set of buttons, and a brief application-specific message,
/// such as status or error information. The message box returns an integer value that indicates which button the user
/// clicked.
/// </summary>
/// <param name="hWnd">
/// A handle to the owner window of the message box to be created. If this parameter is NULL, the message box has no
/// owner window.
/// </param>
/// <param name="text">
/// The message to be displayed. If the string consists of more than one line, you can separate the lines using a carriage
/// return and/or linefeed character between each line.
/// </param>
/// <param name="caption">
/// The dialog box title. If this parameter is NULL, the default title is Error.</param>
/// <param name="type">
/// The contents and behavior of the dialog box. This parameter can be a combination of flags from the following groups
/// of flags.
/// </param>
/// <returns>
/// If a message box has a Cancel button, the function returns the IDCANCEL value if either the ESC key is pressed or
/// the Cancel button is selected. If the message box has no Cancel button, pressing ESC will no effect - unless an
/// MB_OK button is present. If an MB_OK button is displayed and the user presses ESC, the return value will be IDOK.
/// If the function fails, the return value is zero.To get extended error information, call GetLastError. If the function
/// succeeds, the return value is one of the ID* enum values.
/// </returns>
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern int MessageBoxW(IntPtr hWnd, string text, string caption, MessageBoxType type);
}
/// <summary>
/// Native kernel32 functions.
/// </summary>
internal static partial class NativeFunctions
{
/// <summary>
/// MEM_* from memoryapi.
@ -20,14 +240,14 @@ namespace Dalamud.Injector
/// To coalesce two adjacent placeholders, specify MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS. When you coalesce
/// placeholders, lpAddress and dwSize must exactly match those of the placeholder.
/// </summary>
CoalescePlaceholders = 0x00000001,
CoalescePlaceholders = 0x1,
/// <summary>
/// Frees an allocation back to a placeholder (after you've replaced a placeholder with a private allocation using
/// VirtualAlloc2 or Virtual2AllocFromApp). To split a placeholder into two placeholders, specify
/// MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER.
/// </summary>
PreservePlaceholder = 0x00000002,
PreservePlaceholder = 0x2,
/// <summary>
/// Allocates memory charges (from the overall size of memory and the paging files on disk) for the specified reserved
@ -88,7 +308,7 @@ namespace Dalamud.Injector
/// the specified address range is intact. If the function fails, at least some of the data in the address range
/// has been replaced with zeroes. This value cannot be used with any other value. If MEM_RESET_UNDO is called on
/// an address range which was not MEM_RESET earlier, the behavior is undefined. When you specify MEM_RESET, the
/// VirtualAllocEx function ignores the value of flProtect. However, you must still set flProtect to a valid
/// VirtualAllocEx function ignores the value of flProtect. However, you must still set flProtect to a valid
/// protection value, such as PAGE_NOACCESS.
/// </summary>
ResetUndo = 0x1000000,
@ -122,6 +342,28 @@ namespace Dalamud.Injector
LargePages = 0x20000000,
}
/// <summary>
/// Unprefixed flags from CreateRemoteThread.
/// </summary>
[Flags]
public enum CreateThreadFlags
{
/// <summary>
/// The thread runs immediately after creation.
/// </summary>
RunImmediately = 0x0,
/// <summary>
/// The thread is created in a suspended state, and does not run until the ResumeThread function is called.
/// </summary>
CreateSuspended = 0x4,
/// <summary>
/// The dwStackSize parameter specifies the initial reserve size of the stack. If this flag is not specified, dwStackSize specifies the commit size.
/// </summary>
StackSizeParamIsReservation = 0x10000,
}
/// <summary>
/// PAGE_* from memoryapi.
/// </summary>
@ -198,7 +440,7 @@ namespace Dalamud.Injector
/// The default behavior for VirtualProtect protection change to executable is to mark all locations as valid call
/// targets for CFG.
/// </summary>
TargetsNoUpdate = 0x40000000,
TargetsNoUpdate = TargetsInvalid,
/// <summary>
/// Pages in the region become guard pages. Any attempt to access a guard page causes the system to raise a
@ -312,23 +554,33 @@ namespace Dalamud.Injector
}
/// <summary>
/// Closes an open object handle.
/// WAIT_* from synchapi.
/// </summary>
/// <param name="hObject">
/// A valid handle to an open object.
/// </param>
/// <returns>
/// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.To get extended
/// error information, call GetLastError. If the application is running under a debugger, the function will throw an
/// exception if it receives either a handle value that is not valid or a pseudo-handle value. This can happen if you
/// close a handle twice, or if you call CloseHandle on a handle returned by the FindFirstFile function instead of calling
/// the FindClose function.
/// </returns>
[DllImport("kernel32.dll", SetLastError = true)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
public enum WaitResult
{
/// <summary>
/// The specified object is a mutex object that was not released by the thread that owned the mutex object
/// before the owning thread terminated.Ownership of the mutex object is granted to the calling thread and
/// the mutex state is set to nonsignaled. If the mutex was protecting persistent state information, you
/// should check it for consistency.
/// </summary>
Abandoned = 0x80,
/// <summary>
/// The state of the specified object is signaled.
/// </summary>
Object0 = 0x0,
/// <summary>
/// The time-out interval elapsed, and the object's state is nonsignaled.
/// </summary>
Timeout = 0x102,
/// <summary>
/// The function has failed. To get extended error information, call GetLastError.
/// </summary>
WAIT_FAILED = 0xFFFFFFF,
}
/// <summary>
/// Creates a thread that runs in the virtual address space of another process. Use the CreateRemoteThreadEx function
@ -336,23 +588,23 @@ namespace Dalamud.Injector
/// </summary>
/// <param name="hProcess">
/// A handle to the process in which the thread is to be created. The handle must have the PROCESS_CREATE_THREAD,
/// PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ access rights, and may fail
/// without these rights on certain platforms. For more information, see Process Security and Access Rights.
/// PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ access rights, and may fail without
/// these rights on certain platforms. For more information, see Process Security and Access Rights.
/// </param>
/// <param name="lpThreadAttributes">
/// A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new thread and determines
/// whether child processes can inherit the returned handle. If lpThreadAttributes is NULL, the thread gets a default
/// security descriptor and the handle cannot be inherited. The access control lists (ACL) in the default security descriptor
/// for a thread come from the primary token of the creator.
/// A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new thread and determines whether
/// child processes can inherit the returned handle. If lpThreadAttributes is NULL, the thread gets a default security descriptor
/// and the handle cannot be inherited. The access control lists (ACL) in the default security descriptor for a thread come from
/// the primary token of the creator.
/// </param>
/// <param name="dwStackSize">
/// The initial size of the stack, in bytes. The system rounds this value to the nearest page. If this parameter is
/// 0 (zero), the new thread uses the default size for the executable. For more information, see Thread Stack Size.
/// The initial size of the stack, in bytes. The system rounds this value to the nearest page. If this parameter is 0 (zero), the
/// new thread uses the default size for the executable. For more information, see Thread Stack Size.
/// </param>
/// <param name="lpStartAddress">
/// A pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread and
/// represents the starting address of the thread in the remote process. The function must exist in the remote process.
/// For more information, see ThreadProc.
/// A pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread and represents the
/// starting address of the thread in the remote process. The function must exist in the remote process. For more information,
/// see ThreadProc.
/// </param>
/// <param name="lpParameter">
/// A pointer to a variable to be passed to the thread function.
@ -361,92 +613,43 @@ namespace Dalamud.Injector
/// The flags that control the creation of the thread.
/// </param>
/// <param name="lpThreadId">
/// A pointer to a variable that receives the thread identifier. If this parameter is NULL, the thread identifier is
/// not returned.
/// A pointer to a variable that receives the thread identifier. If this parameter is NULL, the thread identifier is not returned.
/// </param>
/// <returns>
/// If the function succeeds, the return value is a handle to the new thread. If the function fails, the return value
/// is NULL.To get extended error information, call GetLastError. Note that CreateRemoteThread may succeed even if
/// lpStartAddress points to data, code, or is not accessible. If the start address is invalid when the thread runs,
/// an exception occurs, and the thread terminates. Thread termination due to a invalid start address is handled as
/// an error exit for the thread's process. This behavior is similar to the asynchronous nature of CreateProcess, where
/// the process is created even if it refers to invalid or missing dynamic-link libraries (DLL).
/// If the function succeeds, the return value is a handle to the new thread. If the function fails, the return value is
/// NULL.To get extended error information, call GetLastError. Note that CreateRemoteThread may succeed even if lpStartAddress
/// points to data, code, or is not accessible. If the start address is invalid when the thread runs, an exception occurs, and
/// the thread terminates. Thread termination due to a invalid start address is handled as an error exit for the thread's process.
/// This behavior is similar to the asynchronous nature of CreateProcess, where the process is created even if it refers to
/// invalid or missing dynamic-link libraries (DLL).
/// </returns>
[DllImport("kernel32.dll")]
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateRemoteThread(
IntPtr hProcess,
IntPtr lpThreadAttributes,
uint dwStackSize,
UIntPtr dwStackSize,
IntPtr lpStartAddress,
IntPtr lpParameter,
uint dwCreationFlags,
IntPtr lpThreadId);
CreateThreadFlags dwCreationFlags,
out uint lpThreadId);
/// <summary>
/// See https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlew.
/// Retrieves a module handle for the specified module. The module must have been loaded by the calling process. To
/// avoid the race conditions described in the Remarks section, use the GetModuleHandleEx function.
/// Retrieves the termination status of the specified thread.
/// </summary>
/// <param name="lpModuleName">
/// The name of the loaded module (either a .dll or .exe file). If the file name extension is omitted, the default library
/// extension .dll is appended. The file name string can include a trailing point character (.) to indicate that the
/// module name has no extension. The string does not have to specify a path. When specifying a path, be sure to use
/// backslashes (\), not forward slashes (/). The name is compared (case independently) to the names of modules currently
/// mapped into the address space of the calling process. If this parameter is NULL, GetModuleHandle returns a handle
/// to the file used to create the calling process (.exe file). The GetModuleHandle function does not retrieve handles
/// for modules that were loaded using the LOAD_LIBRARY_AS_DATAFILE flag.For more information, see LoadLibraryEx.
/// <param name="hThread">
/// A handle to the thread. The handle must have the THREAD_QUERY_INFORMATION or THREAD_QUERY_LIMITED_INFORMATION
/// access right.For more information, see Thread Security and Access Rights.
/// </param>
/// <param name="lpExitCode">
/// A pointer to a variable to receive the thread termination status.
/// </param>
/// <returns>
/// If the function succeeds, the return value is a handle to the specified module. If the function fails, the return
/// value is NULL.To get extended error information, call GetLastError.
/// </returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
/// <summary>
/// Retrieves the address of an exported function or variable from the specified dynamic-link library (DLL).
/// </summary>
/// <param name="hModule">
/// A handle to the DLL module that contains the function or variable. The LoadLibrary, LoadLibraryEx, LoadPackagedLibrary,
/// or GetModuleHandle function returns this handle. The GetProcAddress function does not retrieve addresses from modules
/// that were loaded using the LOAD_LIBRARY_AS_DATAFILE flag.For more information, see LoadLibraryEx.
/// </param>
/// <param name="procName">
/// The function or variable name, or the function's ordinal value. If this parameter is an ordinal value, it must be
/// in the low-order word; the high-order word must be zero.
/// </param>
/// <returns>
/// If the function succeeds, the return value is the address of the exported function or variable. If the function
/// fails, the return value is NULL.To get extended error information, call GetLastError.
/// </returns>
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
/// <summary>
/// See https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess.
/// Opens an existing local process object.
/// </summary>
/// <param name="processAccess">
/// The access to the process object. This access right is checked against the security descriptor for the process.
/// This parameter can be one or more of the process access rights. If the caller has enabled the SeDebugPrivilege
/// privilege, the requested access is granted regardless of the contents of the security descriptor.
/// </param>
/// <param name="bInheritHandle">
/// If this value is TRUE, processes created by this process will inherit the handle. Otherwise, the processes do
/// not inherit this handle.
/// </param>
/// <param name="processId">
/// The identifier of the local process to be opened.
/// </param>
/// <returns>
/// If the function succeeds, the return value is an open handle to the specified process. If the function fails, the
/// return value is NULL.To get extended error information, call GetLastError.
/// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get
/// extended error information, call GetLastError.
/// </returns>
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(
ProcessAccessFlags processAccess,
bool bInheritHandle,
int processId);
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode);
/// <summary>
/// See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex.
@ -530,6 +733,27 @@ namespace Dalamud.Injector
int dwSize,
AllocationType dwFreeType);
/// <summary>
/// Waits until the specified object is in the signaled state or the time-out interval elapses. To enter an alertable wait
/// state, use the WaitForSingleObjectEx function.To wait for multiple objects, use WaitForMultipleObjects.
/// </summary>
/// <param name="hHandle">
/// A handle to the object. For a list of the object types whose handles can be specified, see the following Remarks section.
/// If this handle is closed while the wait is still pending, the function's behavior is undefined. The handle must have the
/// SYNCHRONIZE access right. For more information, see Standard Access Rights.
/// </param>
/// <param name="dwMilliseconds">
/// The time-out interval, in milliseconds. If a nonzero value is specified, the function waits until the object is signaled
/// or the interval elapses. If dwMilliseconds is zero, the function does not enter a wait state if the object is not signaled;
/// it always returns immediately. If dwMilliseconds is INFINITE, the function will return only when the object is signaled.
/// </param>
/// <returns>
/// If the function succeeds, the return value indicates the event that caused the function to return.
/// It can be one of the WaitResult values.
/// </returns>
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
/// <summary>
/// Writes data to an area of memory in a specified process. The entire area to be written to must be accessible or
/// the operation fails.

View file

@ -1,208 +0,0 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using EasyHook;
using Newtonsoft.Json;
namespace Dalamud.Injector
{
/// <summary>
/// Application entrypoint.
/// </summary>
internal static class Program
{
private static Process process = null;
private static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) =>
{
File.WriteAllText("InjectorException.txt", eventArgs.ExceptionObject.ToString());
#if !DEBUG
MessageBox.Show("Failed to inject the XIVLauncher in-game addon.\nPlease try restarting your game and your PC.\nIf this keeps happening, please report this error.", "XIVLauncher Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
#else
MessageBox.Show("Couldn't inject.\nMake sure that Dalamud was not injected into your target process as a release build before and that the target process can be accessed with VM_WRITE permissions.\n\n" + eventArgs.ExceptionObject, "Debug Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
#endif
Environment.Exit(0);
};
var pid = -1;
if (args.Length >= 1)
{
pid = int.Parse(args[0]);
}
switch (pid)
{
case -1:
process = Process.GetProcessesByName("ffxiv_dx11")[0];
break;
case -2:
process = Process.Start(
"C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\ffxiv_dx11.exe",
"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");
Thread.Sleep(1000);
break;
default:
process = Process.GetProcessById(pid);
break;
}
DalamudStartInfo startInfo;
if (args.Length <= 1)
{
startInfo = GetDefaultStartInfo();
Console.WriteLine("\nA Dalamud start info was not found in the program arguments. One has been generated for you.");
Console.WriteLine("\nCopy the following contents into the program arguments:");
Console.WriteLine();
Console.WriteLine(Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(startInfo))));
}
else
{
startInfo = JsonConvert.DeserializeObject<DalamudStartInfo>(Encoding.UTF8.GetString(Convert.FromBase64String(args[1])));
}
startInfo.WorkingDirectory = Directory.GetCurrentDirectory();
// Seems to help with the STATUS_INTERNAL_ERROR condition
Thread.Sleep(1000);
// Thread.Sleep(10000);
// Inject to process
Inject(process, startInfo);
Thread.Sleep(1000);
#if DEBUG
// Inject exception handler
// NativeInject(process);
#endif
}
private static void Inject(Process process, DalamudStartInfo info)
{
Console.WriteLine($"Injecting to {process.Id}");
// File check
var libPath = Path.GetFullPath("Dalamud.dll");
if (!File.Exists(libPath))
{
Console.WriteLine($"Can't find a dll on {libPath}");
return;
}
RemoteHooking.Inject(process.Id, InjectionOptions.DoNotRequireStrongName, libPath, libPath, info);
Console.WriteLine("Injected");
}
private static void NativeInject(Process process)
{
var libPath = Path.GetFullPath("DalamudDebugStub.dll");
var pathBytes = Encoding.Unicode.GetBytes(libPath);
var len = pathBytes.Length + 1;
Console.WriteLine($"Injecting {libPath}...");
var handle = NativeFunctions.OpenProcess(
NativeFunctions.ProcessAccessFlags.AllAccess,
false,
process.Id);
if (handle == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not OpenProcess");
}
var dllMem = NativeFunctions.VirtualAllocEx(
handle,
IntPtr.Zero,
len,
NativeFunctions.AllocationType.Commit,
NativeFunctions.MemoryProtection.ReadWrite);
if (dllMem == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Could not alloc memory {Marshal.GetLastWin32Error():X}");
}
Console.WriteLine($"dll path at {dllMem.ToInt64():X}");
if (!NativeFunctions.WriteProcessMemory(
handle,
dllMem,
pathBytes,
len,
out var bytesWritten))
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not write DLL");
}
Console.WriteLine($"Wrote {bytesWritten}");
var kernel32 = NativeFunctions.GetModuleHandle("Kernel32.dll");
var loadLibA = NativeFunctions.GetProcAddress(kernel32, "LoadLibraryW");
var remoteThread = NativeFunctions.CreateRemoteThread(
handle,
IntPtr.Zero,
0,
loadLibA,
dllMem,
0,
IntPtr.Zero);
if (remoteThread == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Could not CreateRemoteThread");
}
/*
TODO kill myself
VirtualFreeEx(
handle,
dllMem,
0,
AllocationType.Release);
*/
NativeFunctions.CloseHandle(remoteThread);
NativeFunctions.CloseHandle(handle);
}
private static DalamudStartInfo GetDefaultStartInfo()
{
var ffxivDir = Path.GetDirectoryName(process.MainModule.FileName);
var startInfo = new DalamudStartInfo
{
WorkingDirectory = null,
ConfigurationPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "dalamudConfig.json"),
PluginDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "installedPlugins"),
DefaultPluginDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "devPlugins"),
AssetDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "dalamudAssets"),
GameVersion = File.ReadAllText(Path.Combine(ffxivDir, "ffxivgame.ver")),
Language = ClientLanguage.English,
};
Console.WriteLine("Creating a StartInfo with:\n" +
$"ConfigurationPath: {startInfo.ConfigurationPath}\n" +
$"PluginDirectory: {startInfo.PluginDirectory}\n" +
$"DefaultPluginDirectory: {startInfo.DefaultPluginDirectory}\n" +
$"Language: {startInfo.Language}\n" +
$"GameVersion: {startInfo.GameVersion}\n" +
$"OptOutMbCollection: {startInfo.OptOutMbCollection}\n" +
$"AssetDirectory: {startInfo.AssetDirectory}");
return startInfo;
}
}
}

View file

@ -0,0 +1,79 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using static Dalamud.Injector.NativeFunctions;
namespace Dalamud.Injector
{
/// <summary>
/// Pin an arbitrary string to a remote process.
/// </summary>
internal class RemotePinnedData : IDisposable
{
private readonly Process process;
private readonly byte[] data;
private readonly IntPtr allocAddr;
/// <summary>
/// Initializes a new instance of the <see cref="RemotePinnedData"/> class.
/// </summary>
/// <param name="process">Process to write in.</param>
/// <param name="data">Data to write.</param>
public unsafe RemotePinnedData(Process process, byte[] data)
{
this.process = process;
this.data = data;
this.allocAddr = VirtualAllocEx(
this.process.Handle,
IntPtr.Zero,
this.data.Length,
AllocationType.Commit,
MemoryProtection.ReadWrite);
if (this.allocAddr == IntPtr.Zero || Marshal.GetLastWin32Error() != 0)
{
throw new Exception("Error allocating memory");
}
var result = WriteProcessMemory(
this.process.Handle,
this.allocAddr,
this.data,
this.data.Length,
out _);
if (!result || Marshal.GetLastWin32Error() != 0)
{
throw new Exception("Error writing memory");
}
}
/// <summary>
/// Gets the address of the pinned data.
/// </summary>
public IntPtr Address => this.allocAddr;
/// <inheritdoc/>
public void Dispose()
{
if (this.allocAddr == IntPtr.Zero)
{
return;
}
var result = VirtualFreeEx(
this.process.Handle,
this.allocAddr,
0,
AllocationType.Release);
if (!result || Marshal.GetLastWin32Error() != 0)
{
throw new Exception("Error freeing memory");
}
}
}
}

View file

@ -1,96 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\xunit.runner.console.2.4.1\build\xunit.runner.console.props" Condition="Exists('..\packages\xunit.runner.console.2.4.1\build\xunit.runner.console.props')" />
<Import Project="..\packages\xunit.runner.visualstudio.2.4.3\build\net452\xunit.runner.visualstudio.props" Condition="Exists('..\packages\xunit.runner.visualstudio.2.4.3\build\net452\xunit.runner.visualstudio.props')" />
<Import Project="..\packages\xunit.core.2.4.1\build\xunit.core.props" Condition="Exists('..\packages\xunit.core.2.4.1\build\xunit.core.props')" />
<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>{C8004563-1806-4329-844F-0EF6274291FC}</ProjectGuid>
<ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Dalamud.Test</RootNamespace>
<AssemblyName>Dalamud.Test</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c">
<HintPath>..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.assert, Version=2.4.1.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c">
<HintPath>..\packages\xunit.assert.2.4.1\lib\netstandard1.1\xunit.assert.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.core, Version=2.4.1.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c">
<HintPath>..\packages\xunit.extensibility.core.2.4.1\lib\net452\xunit.core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.execution.desktop, Version=2.4.1.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c">
<HintPath>..\packages\xunit.extensibility.execution.2.4.1\lib\net452\xunit.execution.desktop.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="LocalizationTests.cs" />
<Compile Include="Game\Text\Sanitizer\SanitizerTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Dalamud\Dalamud.csproj">
<Project>{b92dab43-2279-4a2c-96e3-d9d5910edbea}</Project>
<Name>Dalamud</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Analyzer Include="..\packages\xunit.analyzers.0.10.0\analyzers\dotnet\cs\xunit.analyzers.dll" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Target">
<TargetFramework>net5.0-windows</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PlatformTarget>x64</PlatformTarget>
<Platforms>x64;AnyCPU</Platforms>
<LangVersion>9.0</LangVersion>
</PropertyGroup>
<Error Condition="!Exists('..\packages\xunit.core.2.4.1\build\xunit.core.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.core.2.4.1\build\xunit.core.props'))" />
<Error Condition="!Exists('..\packages\xunit.core.2.4.1\build\xunit.core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.core.2.4.1\build\xunit.core.targets'))" />
<Error Condition="!Exists('..\packages\xunit.runner.visualstudio.2.4.3\build\net452\xunit.runner.visualstudio.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.runner.visualstudio.2.4.3\build\net452\xunit.runner.visualstudio.props'))" />
<Error Condition="!Exists('..\packages\xunit.runner.console.2.4.1\build\xunit.runner.console.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.runner.console.2.4.1\build\xunit.runner.console.props'))" />
</Target>
<Import Project="..\packages\xunit.core.2.4.1\build\xunit.core.targets" Condition="Exists('..\packages\xunit.core.2.4.1\build\xunit.core.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
<PropertyGroup Label="Feature">
<RootNamespace>Dalamud.Test</RootNamespace>
<AssemblyTitle>Dalamud.Test</AssemblyTitle>
<AssemblyName>Dalamud.Test</AssemblyName>
<Product>Dalamud.Test</Product>
<Description>Unit tests for Dalamud</Description>
<Company>goatcorp</Company>
<Copyright>Copyright © goatcorp 2021</Copyright>
</PropertyGroup>
<PropertyGroup Label="Output">
<OutputType>Library</OutputType>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition=" '$(Configuration)' == 'Release' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Dalamud\Dalamud.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="0.10.0" />
<PackageReference Include="xunit.assert" Version="2.4.1" />
<PackageReference Include="xunit.core" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
<PackageReference Include="xunit.runner.console" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View file

@ -0,0 +1,58 @@
using Dalamud.Game;
using Xunit;
namespace Dalamud.Test.Game
{
public class GameVersionTests
{
[Theory]
[InlineData("any", "any")]
[InlineData("2021.01.01.0000.0000", "2021.01.01.0000.0000")]
public void VersionEquality(string ver1, string ver2)
{
var v1 = GameVersion.Parse(ver1);
var v2 = GameVersion.Parse(ver2);
Assert.Equal(v1, v2);
}
[Theory]
[InlineData("2020.06.15.0000.0000", "any")]
[InlineData("2021.01.01.0000.0000", "2021.01.01.0000.0001")]
[InlineData("2021.01.01.0000.0000", "2021.01.01.0001.0000")]
[InlineData("2021.01.01.0000.0000", "2021.01.02.0000.0000")]
[InlineData("2021.01.01.0000.0000", "2021.02.01.0000.0000")]
[InlineData("2021.01.01.0000.0000", "2022.01.01.0000.0000")]
public void VersionComparison(string ver1, string ver2)
{
var v1 = GameVersion.Parse(ver1);
var v2 = GameVersion.Parse(ver2);
Assert.True(v1.CompareTo(v2) < 0);
}
[Theory]
[InlineData("2020.06.15.0000.0000")]
[InlineData("2021.01.01.0000")]
[InlineData("2021.01.01")]
[InlineData("2021.01")]
[InlineData("2021")]
public void VersionConstructor(string ver)
{
var v = GameVersion.Parse(ver);
Assert.True(v != null);
}
[Theory]
[InlineData("2020.06.15.0000.0000.0000")]
[InlineData("")]
public void VersionConstructorInvalid(string ver)
{
var result = GameVersion.TryParse(ver, out var v);
Assert.False(result);
Assert.Null(v);
}
}
}

View file

@ -1,29 +1,29 @@
// ReSharper disable StringLiteralTypo
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace Dalamud.Test.Game.Text.Sanitizer {
public class SanitizerTests {
// ReSharper disable StringLiteralTypo
namespace Dalamud.Test.Game.Text.Sanitizer
{
public class SanitizerTests
{
private global::Dalamud.Game.Text.Sanitizer.Sanitizer sanitizer;
[Theory]
[InlineData( ClientLanguage.English, "Pixie Cotton Hood of Healing", "Pixie Cotton Hood of Healing" )]
[InlineData( ClientLanguage.Japanese, "アラガントームストーン:真理", "アラガントームストーン:真理" )]
[InlineData( ClientLanguage.German, "Anemos-Pan\x02\x16\x01\x03zer\x02\x16\x01\x03hand\x02\x16\x01\x03schu\x02\x16\x01\x03he des Drachenbluts", "Anemos-Panzerhandschuhe des Drachenbluts" )]
[InlineData( ClientLanguage.German, "Bienen-Spatha †", "Bienen-Spatha" )]
[InlineData( ClientLanguage.French, "Le Diademe\x02\x1D\x01\x03: terrains de chasse|Le Diademe\x02\x1D\x01\x03: terrains de chasse", "Le Diademe: terrains de chasse|Le Diademe: terrains de chasse" )]
[InlineData( ClientLanguage.French, "Cuir de bœuf", "Cuir de boeuf" )]
public void StringsAreSanitizedCorrectly(
ClientLanguage clientLanguage, string unsanitizedString, string sanitizedString)
[InlineData(ClientLanguage.English, "Pixie Cotton Hood of Healing", "Pixie Cotton Hood of Healing")]
[InlineData(ClientLanguage.Japanese, "アラガントームストーン:真理", "アラガントームストーン:真理")]
[InlineData(ClientLanguage.German, "Anemos-Pan\x02\x16\x01\x03zer\x02\x16\x01\x03hand\x02\x16\x01\x03schu\x02\x16\x01\x03he des Drachenbluts", "Anemos-Panzerhandschuhe des Drachenbluts")]
[InlineData(ClientLanguage.German, "Bienen-Spatha †", "Bienen-Spatha")]
[InlineData(ClientLanguage.French, "Le Diademe\x02\x1D\x01\x03: terrains de chasse|Le Diademe\x02\x1D\x01\x03: terrains de chasse", "Le Diademe: terrains de chasse|Le Diademe: terrains de chasse")]
[InlineData(ClientLanguage.French, "Cuir de bœuf", "Cuir de boeuf")]
public void StringsAreSanitizedCorrectly(ClientLanguage clientLanguage, string unsanitizedString, string sanitizedString)
{
var sanitizedStrings = new List<string> {unsanitizedString};
var sanitizedStrings = new List<string> { unsanitizedString };
sanitizer = new global::Dalamud.Game.Text.Sanitizer.Sanitizer(clientLanguage);
Assert.Equal(sanitizedString, sanitizer.Sanitize(unsanitizedString));
Assert.Equal(sanitizedString, sanitizer.Sanitize(sanitizedStrings).First());
sanitizer = new global::Dalamud.Game.Text.Sanitizer.Sanitizer(ClientLanguage.English);
Assert.Equal(sanitizedString, sanitizer.Sanitize(unsanitizedString, clientLanguage));
Assert.Equal(sanitizedString, sanitizer.Sanitize(sanitizedStrings, clientLanguage).First());

View file

@ -1,20 +1,24 @@
using System.IO;
using System.IO;
using System.Reflection;
using Xunit;
namespace Dalamud.Test {
public class LocalizationTests {
namespace Dalamud.Test
{
public class LocalizationTests
{
private readonly Localization localization;
private string currentLangCode;
public LocalizationTests() {
public LocalizationTests()
{
var workingDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
this.localization = new Localization(workingDir, "dalamud_");
this.localization.OnLocalizationChanged += code => this.currentLangCode = code;
}
[Fact]
public void SetupWithFallbacks_EventInvoked() {
public void SetupWithFallbacks_EventInvoked()
{
this.localization.SetupWithFallbacks();
Assert.Equal("en", this.currentLangCode);
}

View file

@ -1,35 +0,0 @@
using System.Reflection;
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("Dalamud.Test")]
[assembly: AssemblyDescription("Unit tests for Dalamud")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("goatcorp")]
[assembly: AssemblyProduct("Dalamud.Test")]
[assembly: AssemblyCopyright("Copyright © goatcorp 2021")]
[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("C8004563-1806-4329-844F-0EF6274291FC")]
// 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.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="xunit" version="2.4.1" targetFramework="net472" />
<package id="xunit.abstractions" version="2.0.3" targetFramework="net472" />
<package id="xunit.analyzers" version="0.10.0" targetFramework="net472" />
<package id="xunit.assert" version="2.4.1" targetFramework="net472" />
<package id="xunit.core" version="2.4.1" targetFramework="net472" />
<package id="xunit.extensibility.core" version="2.4.1" targetFramework="net472" />
<package id="xunit.extensibility.execution" version="2.4.1" targetFramework="net472" />
<package id="xunit.runner.console" version="2.4.1" targetFramework="net472" developmentDependency="true" />
<package id="xunit.runner.visualstudio" version="2.4.3" targetFramework="net472" developmentDependency="true" />
</packages>

View file

@ -1,137 +1,162 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29215.179
# Visual Studio Version 17
VisualStudioVersion = 17.0.31410.414
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CEF7D22B-CB85-400E-BD64-349A30E3C097}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.gitignore = .gitignore
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "build", "build\build.csproj", "{94E5B016-02B1-459B-97D9-E783F28764B2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud", "Dalamud\Dalamud.csproj", "{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dalamud.Boot", "Dalamud.Boot\Dalamud.Boot.vcxproj", "{55198DC3-A03D-408E-A8EB-2077780C8576}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.Injector", "Dalamud.Injector\Dalamud.Injector.csproj", "{5B832F73-5F54-4ADC-870F-D0095EF72C9A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImGuiScene", "lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj", "{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dalamud.Injector.Boot", "Dalamud.Injector.Boot\Dalamud.Injector.Boot.vcxproj", "{8874326B-E755-4D13-90B4-59AB263A3E6B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SDL2-CS", "lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj", "{85480198-8711-4355-830E-72FD794AD3F6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImGui.NET-472", "lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj", "{0483026E-C6CE-4B1A-AA68-46544C08140B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.Test", "Dalamud.Test\Dalamud.Test.csproj", "{C8004563-1806-4329-844F-0EF6274291FC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interface", "Interface", "{E15BDA6D-E881-4482-94BA-BE5527E917FF}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DalamudDebugStub", "DalamudDebugStub\DalamudDebugStub.vcxproj", "{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImGui.NET-472", "lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj", "{0483026E-C6CE-4B1A-AA68-46544C08140B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFXIVClientStructs", "lib\FFXIVClientStructs\FFXIVClientStructs.csproj", "{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImGuiScene", "lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj", "{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.Test", "Dalamud.Test\Dalamud.Test.csproj", "{C8004563-1806-4329-844F-0EF6274291FC}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SDL2-CS", "lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj", "{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.CorePlugin", "Dalamud.CorePlugin\Dalamud.CorePlugin.csproj", "{4AFDB34A-7467-4D41-B067-53BC4101D9D0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFXIVClientStructs", "lib\FFXIVClientStructs\FFXIVClientStructs\FFXIVClientStructs.csproj", "{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFXIVClientStructs.Common", "lib\FFXIVClientStructs\FFXIVClientStructs.Common\FFXIVClientStructs.Common.csproj", "{F3F0CC3A-DE2E-403F-88B4-B47C62582477}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFXIVClientStructs.Generators", "lib\FFXIVClientStructs\FFXIVClientStructs.Generators\FFXIVClientStructs.Generators.csproj", "{05AB2F46-268B-4915-806F-DDF813E2D59D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Debug|x64.ActiveCfg = Debug|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Debug|x64.Build.0 = Debug|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Release|Any CPU.Build.0 = Release|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Release|x64.ActiveCfg = Release|Any CPU
{94E5B016-02B1-459B-97D9-E783F28764B2}.Release|x64.Build.0 = Release|Any CPU
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|Any CPU.ActiveCfg = Debug|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|Any CPU.Build.0 = Debug|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|x64.ActiveCfg = Debug|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|x64.Build.0 = Debug|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|x86.ActiveCfg = Debug|Any CPU
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|x86.Build.0 = Debug|Any CPU
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|Any CPU.Build.0 = Release|Any CPU
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|Any CPU.ActiveCfg = Release|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|Any CPU.Build.0 = Release|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|x64.ActiveCfg = Release|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|x64.Build.0 = Release|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|x86.ActiveCfg = Release|Any CPU
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|x86.Build.0 = Release|Any CPU
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|Any CPU.ActiveCfg = Debug|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|Any CPU.Build.0 = Debug|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|x64.ActiveCfg = Debug|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|x64.Build.0 = Debug|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Release|Any CPU.ActiveCfg = Release|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Release|Any CPU.Build.0 = Release|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Release|x64.ActiveCfg = Release|x64
{55198DC3-A03D-408E-A8EB-2077780C8576}.Release|x64.Build.0 = Release|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|Any CPU.ActiveCfg = Debug|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|Any CPU.Build.0 = Debug|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|x64.ActiveCfg = Debug|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|x64.Build.0 = Debug|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|x86.ActiveCfg = Debug|Any CPU
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|x86.Build.0 = Debug|Any CPU
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|Any CPU.Build.0 = Release|Any CPU
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|Any CPU.ActiveCfg = Release|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|Any CPU.Build.0 = Release|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x64.ActiveCfg = Release|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x64.Build.0 = Release|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x86.ActiveCfg = Release|Any CPU
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x86.Build.0 = Release|Any CPU
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x64.ActiveCfg = Debug|Any CPU
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x64.Build.0 = Debug|Any CPU
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x86.ActiveCfg = Debug|Any CPU
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x86.Build.0 = Debug|Any CPU
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.Build.0 = Release|Any CPU
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x64.ActiveCfg = Release|Any CPU
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x64.Build.0 = Release|Any CPU
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x86.ActiveCfg = Release|Any CPU
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x86.Build.0 = Release|Any CPU
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|x64.ActiveCfg = Debug|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|x64.Build.0 = Debug|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|x86.ActiveCfg = Debug|x86
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|x86.Build.0 = Debug|x86
{85480198-8711-4355-830E-72FD794AD3F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85480198-8711-4355-830E-72FD794AD3F6}.Release|Any CPU.Build.0 = Release|Any CPU
{85480198-8711-4355-830E-72FD794AD3F6}.Release|x64.ActiveCfg = Release|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Release|x64.Build.0 = Release|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Release|x86.ActiveCfg = Release|x86
{85480198-8711-4355-830E-72FD794AD3F6}.Release|x86.Build.0 = Release|x86
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.ActiveCfg = Debug|Any CPU
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.Build.0 = Debug|Any CPU
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x86.ActiveCfg = Debug|Any CPU
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x86.Build.0 = Debug|Any CPU
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.Build.0 = Release|Any CPU
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.ActiveCfg = Release|Any CPU
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.Build.0 = Release|Any CPU
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x86.ActiveCfg = Release|Any CPU
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x86.Build.0 = Release|Any CPU
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Debug|Any CPU.ActiveCfg = Debug|x64
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Debug|x64.ActiveCfg = Debug|x64
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Debug|x86.ActiveCfg = Debug|Win32
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|Any CPU.ActiveCfg = Release|x64
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|Any CPU.Build.0 = Release|x64
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|x64.ActiveCfg = Release|x64
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|x64.Build.0 = Release|x64
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|x86.ActiveCfg = Release|Win32
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|x86.Build.0 = Release|Win32
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|x64.ActiveCfg = Debug|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|x64.Build.0 = Debug|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|x86.ActiveCfg = Debug|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|x86.Build.0 = Debug|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|Any CPU.Build.0 = Release|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|x64.ActiveCfg = Release|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|x64.Build.0 = Release|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|x86.ActiveCfg = Release|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|x86.Build.0 = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x64.ActiveCfg = Debug|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x64.Build.0 = Debug|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x86.ActiveCfg = Debug|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x86.Build.0 = Debug|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.Build.0 = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x64.ActiveCfg = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x64.Build.0 = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x86.ActiveCfg = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x86.Build.0 = Release|Any CPU
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|Any CPU.ActiveCfg = Debug|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|x64.ActiveCfg = Debug|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|x64.Build.0 = Debug|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|Any CPU.ActiveCfg = Release|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|x64.ActiveCfg = Release|x64
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|x64.Build.0 = Release|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.ActiveCfg = Debug|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.Build.0 = Debug|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x64.ActiveCfg = Debug|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x64.Build.0 = Debug|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.ActiveCfg = Release|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.Build.0 = Release|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x64.ActiveCfg = Release|x64
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x64.Build.0 = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.ActiveCfg = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.Build.0 = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.ActiveCfg = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.Build.0 = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.ActiveCfg = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.Build.0 = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.ActiveCfg = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.Build.0 = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.ActiveCfg = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.Build.0 = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x64.ActiveCfg = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x64.Build.0 = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.ActiveCfg = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.Build.0 = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x64.ActiveCfg = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x64.Build.0 = Release|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|Any CPU.ActiveCfg = Debug|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|Any CPU.Build.0 = Debug|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|x64.ActiveCfg = Debug|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|x64.Build.0 = Debug|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|Any CPU.ActiveCfg = Release|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|Any CPU.Build.0 = Release|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|x64.ActiveCfg = Release|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|x64.Build.0 = Release|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|x64.ActiveCfg = Debug|Any CPU
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|x64.Build.0 = Debug|Any CPU
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|Any CPU.Build.0 = Release|Any CPU
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|x64.ActiveCfg = Release|Any CPU
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|x64.Build.0 = Release|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|x64.ActiveCfg = Debug|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|x64.Build.0 = Debug|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|Any CPU.Build.0 = Release|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|x64.ActiveCfg = Release|Any CPU
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|x64.Build.0 = Release|Any CPU
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Debug|x64.ActiveCfg = Debug|Any CPU
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Debug|x64.Build.0 = Debug|Any CPU
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Release|Any CPU.Build.0 = Release|Any CPU
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Release|x64.ActiveCfg = Release|Any CPU
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Release|x64.Build.0 = Release|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|x64.ActiveCfg = Debug|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|x64.Build.0 = Debug|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|Any CPU.Build.0 = Release|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|x64.ActiveCfg = Release|Any CPU
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{85480198-8711-4355-830E-72FD794AD3F6} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{0483026E-C6CE-4B1A-AA68-46544C08140B} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{F3F0CC3A-DE2E-403F-88B4-B47C62582477} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{05AB2F46-268B-4915-806F-DDF813E2D59D} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {79B65AC9-C940-410E-AB61-7EA7E12C7599}

View file

@ -7,19 +7,19 @@ using Newtonsoft.Json;
using Serilog;
using Serilog.Events;
namespace Dalamud.Configuration
namespace Dalamud.Configuration.Internal
{
/// <summary>
/// Class containing Dalamud settings.
/// </summary>
[Serializable]
public class DalamudConfiguration
internal sealed class DalamudConfiguration
{
[JsonIgnore]
private string configPath;
/// <summary>
/// Delegate for the <see cref="DalamudConfiguration.OnDalamudConfigurationSaved"/> event that occurs when the dalamud configuration is saved.
/// Delegate for the <see cref="OnDalamudConfigurationSaved"/> event that occurs when the dalamud configuration is saved.
/// </summary>
/// <param name="dalamudConfiguration">The current dalamud configuration.</param>
public delegate void DalamudConfigurationSavedDelegate(DalamudConfiguration dalamudConfiguration);
@ -69,15 +69,25 @@ namespace Dalamud.Configuration
/// </summary>
public bool DoDalamudTest { get; set; }
/// <summary>
/// Gets or sets a value indicating whether or not XL should download the Dalamud .NET runtime.
/// </summary>
public bool DoDalamudRuntime { get; set; }
/// <summary>
/// Gets or sets a list of custom repos.
/// </summary>
public List<ThirdRepoSetting> ThirdRepoList { get; set; } = new List<ThirdRepoSetting>();
public List<ThirdPartyRepoSettings> ThirdRepoList { get; set; } = new();
/// <summary>
/// Gets or sets a list of hidden plugins.
/// </summary>
public List<string> HiddenPluginInternalName { get; set; } = new List<string>();
public List<string> HiddenPluginInternalName { get; set; } = new();
/// <summary>
/// Gets or sets a list of additional settings for devPlugins.
/// </summary>
public List<DevPluginSettings> DevPluginSettings { get; set; } = new();
/// <summary>
/// Gets or sets the global UI scale.
@ -150,7 +160,7 @@ namespace Dalamud.Configuration
public bool IsAntiAntiDebugEnabled { get; set; } = false;
/// <summary>
/// Specifies the kind of beta to download when <see cref="DoDalamudTest"/> is set to true.
/// Gets or sets the kind of beta to download when <see cref="DoDalamudTest"/> is set to true.
/// </summary>
public string DalamudBetaKind { get; set; }

View file

@ -0,0 +1,33 @@
namespace Dalamud.Configuration.Internal
{
/// <summary>
/// Settings for DevPlugins.
/// </summary>
internal sealed class DevPluginSettings
{
/// <summary>
/// Initializes a new instance of the <see cref="DevPluginSettings"/> class.
/// </summary>
/// <param name="dllFile">Filename of the DLL representing this plugin.</param>
public DevPluginSettings(string dllFile)
{
this.DllFile = dllFile;
}
/// <summary>
/// Gets or sets the path to a plugin DLL. This is automatically generated for any plugins in the devPlugins folder. However by
/// specifiying this value manually, you can add arbitrary files outside the normal file paths.
/// </summary>
public string DllFile { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this plugin should automatically start when Dalamud boots up.
/// </summary>
public bool StartOnBoot { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether this plugin should automatically reload on file change.
/// </summary>
public bool AutomaticReloading { get; set; } = false;
}
}

View file

@ -3,7 +3,7 @@ namespace Dalamud.Configuration
/// <summary>
/// Third party repository for dalamud plugins.
/// </summary>
public class ThirdRepoSetting
internal sealed class ThirdPartyRepoSettings
{
/// <summary>
/// Gets or sets the third party repo url.
@ -16,16 +16,14 @@ namespace Dalamud.Configuration
public bool IsEnabled { get; set; }
/// <summary>
/// Create new instance of third party repo object.
/// Gets or sets a short name for the repo url.
/// </summary>
/// <returns>New instance of third party repo.</returns>
public ThirdRepoSetting Clone()
{
return new ThirdRepoSetting
{
Url = this.Url,
IsEnabled = this.IsEnabled,
};
}
public string Name { get; set; }
/// <summary>
/// Clone this object.
/// </summary>
/// <returns>A shallow copy of this object.</returns>
public ThirdPartyRepoSettings Clone() => this.MemberwiseClone() as ThirdPartyRepoSettings;
}
}

View file

@ -7,7 +7,7 @@ namespace Dalamud.Configuration
/// <summary>
/// Configuration to store settings for a dalamud plugin.
/// </summary>
public class PluginConfigurations
public sealed class PluginConfigurations
{
private readonly DirectoryInfo configDirectory;

View file

@ -1,10 +1,10 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Configuration;
using Dalamud.Configuration.Internal;
using Dalamud.Data;
using Dalamud.Game;
using Dalamud.Game.Addon;
@ -13,17 +13,24 @@ using Dalamud.Game.Command;
using Dalamud.Game.Internal;
using Dalamud.Game.Network;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Interface;
using Dalamud.Plugin;
using Dalamud.Hooking.Internal;
using Dalamud.Interface.Internal;
using Dalamud.Memory;
using Dalamud.Plugin.Internal;
using Serilog;
using Serilog.Core;
#if DEBUG
// This allows for rapid prototyping of Dalamud modules with access to internal objects.
[assembly: InternalsVisibleTo("Dalamud.CorePlugin")]
#endif
namespace Dalamud
{
/// <summary>
/// The main Dalamud class containing all subsystems.
/// </summary>
public sealed class Dalamud : IDisposable
internal sealed class Dalamud : IDisposable
{
#region Internals
@ -31,8 +38,6 @@ namespace Dalamud
private readonly ManualResetEvent finishUnloadSignal;
private readonly string baseDirectory;
private bool hasDisposedPlugins = false;
#endregion
@ -46,11 +51,14 @@ namespace Dalamud
/// <param name="configuration">The Dalamud configuration.</param>
public Dalamud(DalamudStartInfo info, LoggingLevelSwitch loggingLevelSwitch, ManualResetEvent finishSignal, DalamudConfiguration configuration)
{
#if DEBUG
Instance = this;
#endif
this.StartInfo = info;
this.LogLevelSwitch = loggingLevelSwitch;
this.Configuration = configuration;
this.baseDirectory = info.WorkingDirectory;
// this.baseDirectory = info.WorkingDirectory;
this.unloadSignal = new ManualResetEvent(false);
this.unloadSignal.Reset();
@ -59,6 +67,13 @@ namespace Dalamud
this.finishUnloadSignal.Reset();
}
#if DEBUG
/// <summary>
/// Gets the Dalamud singleton instance.
/// </summary>
internal static Dalamud Instance { get; private set; }
#endif
#region Native Game Subsystems
/// <summary>
@ -76,6 +91,11 @@ namespace Dalamud
/// </summary>
internal WinSockHandlers WinSock2 { get; private set; }
/// <summary>
/// Gets Hook management subsystem.
/// </summary>
internal HookManager HookManager { get; private set; }
/// <summary>
/// Gets ImGui Interface subsystem.
/// </summary>
@ -95,11 +115,6 @@ namespace Dalamud
/// </summary>
internal PluginManager PluginManager { get; private set; }
/// <summary>
/// Gets Plugin Repository subsystem.
/// </summary>
internal PluginRepository PluginRepository { get; private set; }
/// <summary>
/// Gets Data provider subsystem.
/// </summary>
@ -205,6 +220,7 @@ namespace Dalamud
// Initialize the process information.
this.TargetModule = Process.GetCurrentProcess().MainModule;
this.SigScanner = new SigScanner(this.TargetModule, true);
this.HookManager = new HookManager(this);
// Initialize game subsystem
this.Framework = new Framework(this.SigScanner, this);
@ -289,6 +305,7 @@ namespace Dalamud
Log.Information("[T2] Data OK!");
this.SeStringManager = new SeStringManager(this.Data);
MemoryHelper.Initialize(this); // For SeString handling
Log.Information("[T2] SeString OK!");
@ -327,28 +344,18 @@ namespace Dalamud
{
Log.Information("[T3] START!");
this.PluginRepository =
new PluginRepository(this, this.StartInfo.PluginDirectory, this.StartInfo.GameVersion);
Log.Information("[T3] PREPO OK!");
if (!bool.Parse(Environment.GetEnvironmentVariable("DALAMUD_NOT_HAVE_PLUGINS") ?? "false"))
{
try
{
this.PluginRepository.CleanupPlugins();
Log.Information("[T3] PRC OK!");
this.PluginManager = new PluginManager(
this,
this.StartInfo.PluginDirectory,
this.StartInfo.DefaultPluginDirectory);
this.PluginManager.LoadSynchronousPlugins();
Task.Run(() => this.PluginManager.LoadDeferredPlugins());
this.PluginManager = new PluginManager(this);
Log.Information("[T3] PM OK!");
this.PluginManager.CleanupPlugins();
Log.Information("[T3] PMC OK!");
this.PluginManager.LoadAllPlugins();
Log.Information("[T3] PML OK!");
}
catch (Exception ex)
{
@ -357,8 +364,6 @@ namespace Dalamud
}
this.DalamudUi = new DalamudInterface(this);
this.InterfaceManager.OnDraw += this.DalamudUi.Draw;
Log.Information("[T3] DUI OK!");
Troubleshooting.LogTroubleshooting(this, this.InterfaceManager != null);
@ -410,16 +415,9 @@ namespace Dalamud
// use any resources that it freed in its own Dispose method
this.InterfaceManager?.Dispose();
try
{
this.PluginManager.UnloadPlugins();
}
catch (Exception ex)
{
Log.Error(ex, "Plugin unload failed.");
}
this.DalamudUi?.Dispose();
this.PluginManager?.Dispose();
}
/// <summary>
@ -436,20 +434,23 @@ namespace Dalamud
}
this.Framework?.Dispose();
this.ClientState?.Dispose();
this.unloadSignal?.Dispose();
this.WinSock2?.Dispose();
this.SigScanner?.Dispose();
this.Data?.Dispose();
this.AntiDebug?.Dispose();
this.SystemMenu?.Dispose();
this.HookManager?.Dispose();
this.SigScanner?.Dispose();
Log.Debug("Dalamud::Dispose() OK!");
}
catch (Exception ex)

View file

@ -1,105 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Target">
<PlatformTarget>AnyCPU</PlatformTarget>
<TargetFramework>net472</TargetFramework>
<TargetFramework>net5.0-windows</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<Platforms>x64;AnyCPU</Platforms>
<LangVersion>9.0</LangVersion>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<PropertyGroup Label="Build">
<OutputType>Library</OutputType>
<OutputPath></OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<DocumentationFile>$(SolutionDir)\bin\Dalamud.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Label="Feature">
<DalamudVersion>5.2.7.0</DalamudVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>XIV Launcher addon framework</Description>
<AssemblyVersion>$(DalamudVersion)</AssemblyVersion>
<Version>$(DalamudVersion)</Version>
<FileVersion>$(DalamudVersion)</FileVersion>
</PropertyGroup>
<ItemGroup Label="Resources">
<None Include="$(SolutionDir)/Resources/**/*" CopyToOutputDirectory="PreserveNewest" Visible="false" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<AppOutputBase>$(MSBuildProjectDirectory)\</AppOutputBase>
<PathMap>$(AppOutputBase)=C:\goatsoft\companysecrets\dalamud\</PathMap>
<PropertyGroup Label="Output">
<OutputType>Library</OutputType>
<OutputPath>..\bin\$(Configuration)\</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
</PropertyGroup>
<PropertyGroup Label="Documentation">
<DocumentationFile>$(OutputPath)Dalamud.xml</DocumentationFile>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Label="Build">
<UseWindowsForms>true</UseWindowsForms>
<EnableDynamicLoading>true</EnableDynamicLoading>
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Deterministic>true</Deterministic>
<Nullable>annotations</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PropertyGroup Label="Configuration">
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PropertyGroup Label="Configuration" Condition="'$(Configuration)'=='Debug'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<NoWarn>IDE0017;IDE0044;IDE0047;IDE0048;IDE1006;CS1573;CS1591;CS1701;CS1702</NoWarn>
<!-- IDE0017 - Use object initializers -->
<!-- IDE0044 - Add readonly modifier -->
<!-- IDE0047 - Parentheses preferences -->
<!-- IDE0048 - Parentheses preferences -->
<!-- IDE1006 - Naming preferences -->
<!-- CS1573 - Parameter has no matching param tag in the XML comment -->
<PropertyGroup Label="Configuration" Condition="'$(Configuration)'=='Release'">
<AppOutputBase>$(MSBuildProjectDirectory)\</AppOutputBase>
<PathMap>$(AppOutputBase)=C:\goatsoft\companysecrets\dalamud\</PathMap>
</PropertyGroup>
<PropertyGroup Label="Warnings">
<NoWarn>IDE0003;IDE1006;CS1591;CS1701;CS1702</NoWarn>
<!-- IDE1006 - Naming violation -->
<!-- CS1591 - Missing XML comment for publicly visible type or member -->
<!-- CS1701 - Runtime policy may be needed -->
<!-- CS1702 - Runtime policy may be needed -->
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\Lumina.Generated.dll" />
<None Remove="stylecop.json" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="stylecop.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CheapLoc" Version="1.1.3" />
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" />
<PackageReference Include="Lumina" Version="3.1.0" />
<PackageReference Include="CheapLoc" Version="1.1.5" />
<PackageReference Include="CoreHook" Version="1.0.4" />
<PackageReference Include="JetBrains.Annotations" Version="2021.1.0" />
<PackageReference Include="Lib.Harmony" Version="2.1.0" />
<PackageReference Include="Lumina" Version="3.3.0" />
<PackageReference Include="Lumina.Excel" Version="5.50.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="PropertyChanged.Fody" Version="2.6.1" />
<PackageReference Include="Serilog" Version="2.6.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="4.0.0" />
<PackageReference Include="EasyHook" Version="2.7.6270" />
<PackageReference Include="SharpDX.Desktop" Version="4.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.10.0" />
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.333">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
<PackageReference Include="System.Drawing.Common" Version="5.0.2" />
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="5.0.1" />
<PackageReference Include="System.Resources.Extensions" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Net.Http" />
<ProjectReference Include="..\lib\FFXIVClientStructs\FFXIVClientStructs\FFXIVClientStructs.csproj" />
<ProjectReference Include="..\lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj" />
<ProjectReference Include="..\lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj" />
<ProjectReference Include="..\lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\lib\FFXIVClientStructs\FFXIVClientStructs.csproj" />
<ProjectReference Include="..\lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj" />
<ProjectReference Include="..\lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj" />
<ProjectReference Include="..\lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj" />
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\Lumina.Generated.dll">
<None Include="corehook64.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>Lumina.Generated.dll</TargetPath>
</ContentWithTargetPath>
</None>
</ItemGroup>
<Target Name="AddRuntimeDependenciesToContent" BeforeTargets="GetCopyToOutputDirectoryItems" DependsOnTargets="GenerateBuildDependencyFile;GenerateBuildRuntimeConfigurationFiles">
<ItemGroup>
<ContentWithTargetPath Include="$(ProjectDepsFilePath)" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectDepsFileName)" />
<ContentWithTargetPath Include="$(ProjectRuntimeConfigFilePath)" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectRuntimeConfigFileName)" />
</ItemGroup>
</Target>
<Target Name="GetGitHash" BeforeTargets="WriteGitHash" Condition="'$(BuildHash)' == ''">
<PropertyGroup>
<!-- temp file for the git version (lives in "obj" folder)-->

View file

@ -1,12 +1,15 @@
using System;
using Dalamud.Game;
using Newtonsoft.Json;
namespace Dalamud
{
/// <summary>
/// Class containing information needed to initialize Dalamud.
/// Struct containing information needed to initialize Dalamud.
/// </summary>
[Serializable]
public sealed class DalamudStartInfo
public struct DalamudStartInfo
{
/// <summary>
/// The working directory of the XIVLauncher installations.
@ -41,7 +44,8 @@ namespace Dalamud
/// <summary>
/// The current game version code.
/// </summary>
public string GameVersion;
[JsonConverter(typeof(GameVersionConverter))]
public GameVersion GameVersion;
/// <summary>
/// Whether or not market board information should be uploaded by default.

View file

@ -7,6 +7,7 @@ using System.Threading;
using Dalamud.Data.LuminaExtensions;
using Dalamud.Interface;
using Dalamud.Interface.Internal;
using ImGuiScene;
using JetBrains.Annotations;
using Lumina;
@ -21,7 +22,7 @@ namespace Dalamud.Data
/// <summary>
/// This class provides data for Dalamud-internal features, but can also be used by plugins if needed.
/// </summary>
public class DataManager : IDisposable
public sealed class DataManager : IDisposable
{
private const string IconFileFormat = "ui/icon/{0:D3}000/{1}{2:D6}.tex";
private readonly InterfaceManager interfaceManager;
@ -32,6 +33,7 @@ namespace Dalamud.Data
private GameData gameData;
private Thread luminaResourceThread;
private CancellationTokenSource luminaCancellationTokenSource;
/// <summary>
/// Initializes a new instance of the <see cref="DataManager"/> class.
@ -41,8 +43,9 @@ namespace Dalamud.Data
internal DataManager(ClientLanguage language, InterfaceManager interfaceManager)
{
this.interfaceManager = interfaceManager;
// Set up default values so plugins do not null-reference when data is being loaded.
this.ServerOpCodes = new ReadOnlyDictionary<string, ushort>(new Dictionary<string, ushort>());
this.ClientOpCodes = this.ServerOpCodes = new ReadOnlyDictionary<string, ushort>(new Dictionary<string, ushort>());
this.Language = language;
}
@ -99,7 +102,7 @@ namespace Dalamud.Data
ClientLanguage.English => Lumina.Data.Language.English,
ClientLanguage.German => Lumina.Data.Language.German,
ClientLanguage.French => Lumina.Data.Language.French,
_ => throw new ArgumentOutOfRangeException(nameof(this.Language), $"Unknown Language: {this.Language}"),
_ => throw new ArgumentOutOfRangeException(nameof(language), $"Unknown Language: {language}"),
};
return this.Excel.GetSheet<T>(lang);
}
@ -162,7 +165,7 @@ namespace Dalamud.Data
ClientLanguage.English => "en/",
ClientLanguage.German => "de/",
ClientLanguage.French => "fr/",
_ => throw new ArgumentOutOfRangeException(nameof(this.Language), $"Unknown Language: {this.Language}"),
_ => throw new ArgumentOutOfRangeException(nameof(iconLanguage), $"Unknown Language: {iconLanguage}"),
};
return this.GetIcon(type, iconId);
@ -232,7 +235,7 @@ namespace Dalamud.Data
/// </summary>
public void Dispose()
{
this.luminaResourceThread.Abort();
this.luminaCancellationTokenSource.Cancel();
}
/// <summary>
@ -245,14 +248,14 @@ namespace Dalamud.Data
{
Log.Verbose("Starting data load...");
var zoneOpCodeDict =
JsonConvert.DeserializeObject<Dictionary<string, ushort>>(File.ReadAllText(Path.Combine(baseDir, "UIRes", "serveropcode.json")));
var zoneOpCodeDict = JsonConvert.DeserializeObject<Dictionary<string, ushort>>(
File.ReadAllText(Path.Combine(baseDir, "UIRes", "serveropcode.json")));
this.ServerOpCodes = new ReadOnlyDictionary<string, ushort>(zoneOpCodeDict);
Log.Verbose("Loaded {0} ServerOpCodes.", zoneOpCodeDict.Count);
var clientOpCodeDict =
JsonConvert.DeserializeObject<Dictionary<string, ushort>>(File.ReadAllText(Path.Combine(baseDir, "UIRes", "clientopcode.json")));
var clientOpCodeDict = JsonConvert.DeserializeObject<Dictionary<string, ushort>>(
File.ReadAllText(Path.Combine(baseDir, "UIRes", "clientopcode.json")));
this.ClientOpCodes = new ReadOnlyDictionary<string, ushort>(clientOpCodeDict);
Log.Verbose("Loaded {0} ClientOpCodes.", clientOpCodeDict.Count);
@ -273,9 +276,7 @@ namespace Dalamud.Data
ClientLanguage.English => Lumina.Data.Language.English,
ClientLanguage.German => Lumina.Data.Language.German,
ClientLanguage.French => Lumina.Data.Language.French,
_ => throw new ArgumentOutOfRangeException(
nameof(this.Language),
@"Unknown Language: " + this.Language),
_ => throw new ArgumentOutOfRangeException(nameof(this.Language), $"Unknown Language: {this.Language}"),
},
};
@ -289,9 +290,12 @@ namespace Dalamud.Data
this.IsDataReady = true;
this.luminaResourceThread = new Thread(() =>
this.luminaCancellationTokenSource = new();
var luminaCancellationToken = this.luminaCancellationTokenSource.Token;
this.luminaResourceThread = new(() =>
{
while (true)
while (!luminaCancellationToken.IsCancellationRequested)
{
if (this.gameData.FileHandleManager.HasPendingFileLoads)
{
@ -302,8 +306,6 @@ namespace Dalamud.Data
Thread.Sleep(5);
}
}
// ReSharper disable once FunctionNeverReturns
});
this.luminaResourceThread.Start();
}

View file

@ -1,12 +1,13 @@
using System;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Configuration;
using Dalamud.Interface;
using EasyHook;
using Dalamud.Configuration.Internal;
using Dalamud.Interface.Internal;
using Newtonsoft.Json;
using Serilog;
using Serilog.Core;
using Serilog.Events;
@ -16,31 +17,41 @@ namespace Dalamud
/// <summary>
/// The main entrypoint for the Dalamud system.
/// </summary>
public sealed class EntryPoint : IEntryPoint
public sealed class EntryPoint
{
/// <summary>
/// Initializes a new instance of the <see cref="EntryPoint"/> class.
/// A delegate used during initialization of the CLR from Dalamud.Boot.
/// </summary>
/// <param name="ctx">The <see cref="RemoteHooking.IContext"/> used to load the DLL.</param>
/// <param name="info">The <see cref="DalamudStartInfo"/> containing information needed to initialize Dalamud.</param>
public EntryPoint(RemoteHooking.IContext ctx, DalamudStartInfo info)
/// <param name="infoPtr">Pointer to a serialized <see cref="DalamudStartInfo"/> data.</param>
public delegate void InitDelegate(IntPtr infoPtr);
/// <summary>
/// Initialize Dalamud.
/// </summary>
/// <param name="infoPtr">Pointer to a serialized <see cref="DalamudStartInfo"/> data.</param>
public static void Initialize(IntPtr infoPtr)
{
// Required by EasyHook
var infoStr = Marshal.PtrToStringAnsi(infoPtr);
var info = JsonConvert.DeserializeObject<DalamudStartInfo>(infoStr);
new Thread(() => RunThread(info)).Start();
}
/// <summary>
/// Initialize all Dalamud subsystems and start running on the main thread.
/// </summary>
/// <param name="ctx">The <see cref="RemoteHooking.IContext"/> used to load the DLL.</param>
/// <param name="info">The <see cref="DalamudStartInfo"/> containing information needed to initialize Dalamud.</param>
public void Run(RemoteHooking.IContext ctx, DalamudStartInfo info)
private static void RunThread(DalamudStartInfo info)
{
// Load configuration first to get some early persistent state, like log level
var configuration = DalamudConfiguration.Load(info.ConfigurationPath);
// Setup logger
var (logger, levelSwitch) = this.NewLogger(info.WorkingDirectory, configuration.LogLevel);
Log.Logger = logger;
var levelSwitch = InitLogging(info.WorkingDirectory);
// Log any unhandled exception.
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
var finishSignal = new ManualResetEvent(false);
@ -50,12 +61,7 @@ namespace Dalamud
Log.Information("Initializing a session..");
// This is due to GitHub not supporting TLS 1.0, so we enable all TLS versions globally
System.Net.ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls | SecurityProtocolType.Ssl3;
// Log any unhandled exception.
AppDomain.CurrentDomain.UnhandledException += this.OnUnhandledException;
TaskScheduler.UnobservedTaskException += this.OnUnobservedTaskException;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls;
var dalamud = new Dalamud(info, levelSwitch, finishSignal, configuration);
Log.Information("Starting a session..");
@ -72,7 +78,8 @@ namespace Dalamud
}
finally
{
AppDomain.CurrentDomain.UnhandledException -= this.OnUnhandledException;
TaskScheduler.UnobservedTaskException -= OnUnobservedTaskException;
AppDomain.CurrentDomain.UnhandledException -= OnUnhandledException;
Log.Information("Session has ended.");
Log.CloseAndFlush();
@ -81,7 +88,7 @@ namespace Dalamud
}
}
private (Logger Logger, LoggingLevelSwitch LevelSwitch) NewLogger(string baseDirectory, LogEventLevel logLevel)
private static LoggingLevelSwitch InitLogging(string baseDirectory)
{
#if DEBUG
var logPath = Path.Combine(baseDirectory, "dalamud.log");
@ -96,33 +103,32 @@ namespace Dalamud
#else
levelSwitch.MinimumLevel = logLevel;
#endif
var newLogger = new LoggerConfiguration()
Log.Logger = new LoggerConfiguration()
.WriteTo.Async(a => a.File(logPath))
.WriteTo.Sink(SerilogEventSink.Instance)
.MinimumLevel.ControlledBy(levelSwitch)
.CreateLogger();
return (newLogger, levelSwitch);
return levelSwitch;
}
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs arg)
private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs args)
{
switch (arg.ExceptionObject)
switch (args.ExceptionObject)
{
case Exception ex:
Log.Fatal(ex, "Unhandled exception on AppDomain");
break;
default:
Log.Fatal("Unhandled SEH object on AppDomain: {Object}", arg.ExceptionObject);
Log.Fatal("Unhandled SEH object on AppDomain: {Object}", args.ExceptionObject);
break;
}
}
private void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
private static void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs args)
{
if (!e.Observed)
Log.Error(e.Exception, "Unobserved exception in Task.");
if (!args.Observed)
Log.Error(args.Exception, "Unobserved exception in Task.");
}
}
}

View file

@ -11,6 +11,16 @@
<xs:documentation>Used to control if the On_PropertyName_Changed feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="TriggerDependentProperties" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the Dependent properties feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="EnableIsChangedProperty" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the IsChanged property feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="EventInvokerNames" type="xs:string">
<xs:annotation>
<xs:documentation>Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.</xs:documentation>
@ -31,6 +41,16 @@
<xs:documentation>Used to control if equality checks should use the static Equals method resolved from the base class.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SuppressWarnings" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to turn off build warnings from this weaver.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SuppressOnPropertyNameChangedWarning" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to turn off build warnings about mismatched On_PropertyName_Changed methods.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>

View file

@ -16,11 +16,11 @@ namespace Dalamud.Game.Addon
internal sealed unsafe partial class DalamudSystemMenu
{
private readonly Dalamud dalamud;
private AtkValueChangeType atkValueChangeType;
private AtkValueSetString atkValueSetString;
private Hook<AgentHudOpenSystemMenuPrototype> hookAgentHudOpenSystemMenu;
private readonly AtkValueChangeType atkValueChangeType;
private readonly AtkValueSetString atkValueSetString;
private readonly Hook<AgentHudOpenSystemMenuPrototype> hookAgentHudOpenSystemMenu;
// TODO: Make this into events in Framework.Gui
private Hook<UiModuleRequestMainCommand> hookUiModuleRequestMainCommand;
private readonly Hook<UiModuleRequestMainCommand> hookUiModuleRequestMainCommand;
/// <summary>
/// Initializes a new instance of the <see cref="DalamudSystemMenu"/> class.
@ -34,13 +34,10 @@ namespace Dalamud.Game.Addon
this.hookAgentHudOpenSystemMenu = new Hook<AgentHudOpenSystemMenuPrototype>(openSystemMenuAddress, this.AgentHudOpenSystemMenuDetour);
var atkValueChangeTypeAddress =
this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 45 84 F6 48 8D 4C 24 ??");
this.atkValueChangeType =
Marshal.GetDelegateForFunctionPointer<AtkValueChangeType>(atkValueChangeTypeAddress);
var atkValueChangeTypeAddress = this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 45 84 F6 48 8D 4C 24 ??");
this.atkValueChangeType = Marshal.GetDelegateForFunctionPointer<AtkValueChangeType>(atkValueChangeTypeAddress);
var atkValueSetStringAddress =
this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 41 03 ED");
var atkValueSetStringAddress = this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 41 03 ED");
this.atkValueSetString = Marshal.GetDelegateForFunctionPointer<AtkValueSetString>(atkValueSetStringAddress);
var uiModuleRequestMainCommandAddress = this.dalamud.SigScanner.ScanText("40 53 56 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 48 8B 01 8B DA 48 8B F1 FF 90 ?? ?? ?? ??");
@ -140,10 +137,10 @@ namespace Dalamud.Game.Addon
switch (commandId)
{
case 69420:
this.dalamud.DalamudUi.TogglePluginInstaller();
this.dalamud.DalamudUi.TogglePluginInstallerWindow();
break;
case 69421:
this.dalamud.DalamudUi.ToggleSettings();
this.dalamud.DalamudUi.ToggleSettingsWindow();
break;
default:
this.hookUiModuleRequestMainCommand.Original(thisPtr, commandId);

View file

@ -11,7 +11,7 @@ using CheapLoc;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Interface;
using Dalamud.Interface.Internal.Windows;
using Serilog;
namespace Dalamud.Game
@ -21,39 +21,39 @@ namespace Dalamud.Game
/// </summary>
public class ChatHandlers
{
private static readonly Dictionary<string, string> UnicodeToDiscordEmojiDict = new()
{
{ "", "<:ffxive071:585847382210642069>" },
{ "", "<:ffxive083:585848592699490329>" },
};
// private static readonly Dictionary<string, string> UnicodeToDiscordEmojiDict = new()
// {
// { "", "<:ffxive071:585847382210642069>" },
// { "", "<:ffxive083:585848592699490329>" },
// };
private readonly Dictionary<XivChatType, Color> handledChatTypeColors = new()
{
{ XivChatType.CrossParty, Color.DodgerBlue },
{ XivChatType.Party, Color.DodgerBlue },
{ XivChatType.FreeCompany, Color.DeepSkyBlue },
{ XivChatType.CrossLinkShell1, Color.ForestGreen },
{ XivChatType.CrossLinkShell2, Color.ForestGreen },
{ XivChatType.CrossLinkShell3, Color.ForestGreen },
{ XivChatType.CrossLinkShell4, Color.ForestGreen },
{ XivChatType.CrossLinkShell5, Color.ForestGreen },
{ XivChatType.CrossLinkShell6, Color.ForestGreen },
{ XivChatType.CrossLinkShell7, Color.ForestGreen },
{ XivChatType.CrossLinkShell8, Color.ForestGreen },
{ XivChatType.Ls1, Color.ForestGreen },
{ XivChatType.Ls2, Color.ForestGreen },
{ XivChatType.Ls3, Color.ForestGreen },
{ XivChatType.Ls4, Color.ForestGreen },
{ XivChatType.Ls5, Color.ForestGreen },
{ XivChatType.Ls6, Color.ForestGreen },
{ XivChatType.Ls7, Color.ForestGreen },
{ XivChatType.Ls8, Color.ForestGreen },
{ XivChatType.TellIncoming, Color.HotPink },
{ XivChatType.PvPTeam, Color.SandyBrown },
{ XivChatType.Urgent, Color.DarkViolet },
{ XivChatType.NoviceNetwork, Color.SaddleBrown },
{ XivChatType.Echo, Color.Gray },
};
// private readonly Dictionary<XivChatType, Color> handledChatTypeColors = new()
// {
// { XivChatType.CrossParty, Color.DodgerBlue },
// { XivChatType.Party, Color.DodgerBlue },
// { XivChatType.FreeCompany, Color.DeepSkyBlue },
// { XivChatType.CrossLinkShell1, Color.ForestGreen },
// { XivChatType.CrossLinkShell2, Color.ForestGreen },
// { XivChatType.CrossLinkShell3, Color.ForestGreen },
// { XivChatType.CrossLinkShell4, Color.ForestGreen },
// { XivChatType.CrossLinkShell5, Color.ForestGreen },
// { XivChatType.CrossLinkShell6, Color.ForestGreen },
// { XivChatType.CrossLinkShell7, Color.ForestGreen },
// { XivChatType.CrossLinkShell8, Color.ForestGreen },
// { XivChatType.Ls1, Color.ForestGreen },
// { XivChatType.Ls2, Color.ForestGreen },
// { XivChatType.Ls3, Color.ForestGreen },
// { XivChatType.Ls4, Color.ForestGreen },
// { XivChatType.Ls5, Color.ForestGreen },
// { XivChatType.Ls6, Color.ForestGreen },
// { XivChatType.Ls7, Color.ForestGreen },
// { XivChatType.Ls8, Color.ForestGreen },
// { XivChatType.TellIncoming, Color.HotPink },
// { XivChatType.PvPTeam, Color.SandyBrown },
// { XivChatType.Urgent, Color.DarkViolet },
// { XivChatType.NoviceNetwork, Color.SaddleBrown },
// { XivChatType.Echo, Color.Gray },
// };
private readonly Regex rmtRegex = new(
@"4KGOLD|We have sufficient stock|VPK\.OM|Gil for free|www\.so9\.com|Fast & Convenient|Cheap & Safety Guarantee|【Code|A O A U E|igfans|4KGOLD\.COM|Cheapest Gil with|pvp and bank on google|Selling Cheap GIL|ff14mogstation\.com|Cheap Gil 1000k|gilsforyou|server 1000K =|gils_selling|E A S Y\.C O M|bonus code|mins delivery guarantee|Sell cheap|Salegm\.com|cheap Mog|Off Code:|FF14Mog.com|使用する5オ|Off Code( *):|offers Fantasia",
@ -96,14 +96,14 @@ namespace Dalamud.Game
private readonly Regex urlRegex = new(@"(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?", RegexOptions.Compiled);
private readonly Dalamud dalamud;
private DalamudLinkPayload openInstallerWindowLink;
private readonly DalamudLinkPayload openInstallerWindowLink;
private bool hasSeenLoadingMsg;
/// <summary>
/// Initializes a new instance of the <see cref="ChatHandlers"/> class.
/// </summary>
/// <param name="dalamud">Dalamud instance.</param>
public ChatHandlers(Dalamud dalamud)
internal ChatHandlers(Dalamud dalamud)
{
this.dalamud = dalamud;
@ -121,24 +121,24 @@ namespace Dalamud.Game
/// </summary>
public string LastLink { get; private set; }
/// <summary>
/// Convert a string to SeString and wrap in italics payloads.
/// </summary>
/// <param name="text">Text to convert.</param>
/// <returns>SeString payload of italicized text.</returns>
private static SeString MakeItalics(string text)
{
// TODO: when the code OnCharMessage is switched to SeString, this can be a straight insertion of the
// italics payloads only, and be a lot cleaner
var italicString = new SeString(new List<Payload>(new Payload[]
{
EmphasisItalicPayload.ItalicsOn,
new TextPayload(text),
EmphasisItalicPayload.ItalicsOff,
}));
return italicString;
}
// /// <summary>
// /// Convert a string to SeString and wrap in italics payloads.
// /// </summary>
// /// <param name="text">Text to convert.</param>
// /// <returns>SeString payload of italicized text.</returns>
// private static SeString MakeItalics(string text)
// {
// // TODO: when the code OnCharMessage is switched to SeString, this can be a straight insertion of the
// // italics payloads only, and be a lot cleaner
// var italicString = new SeString(new List<Payload>(new Payload[]
// {
// EmphasisItalicPayload.ItalicsOn,
// new TextPayload(text),
// EmphasisItalicPayload.ItalicsOff,
// }));
//
// return italicString;
// }
private void OnCheckMessageHandled(XivChatType type, uint senderid, ref SeString sender, ref SeString message, ref bool isHandled)
{
@ -243,13 +243,13 @@ namespace Dalamud.Game
var assemblyVersion = Assembly.GetAssembly(typeof(ChatHandlers)).GetName().Version.ToString();
this.dalamud.Framework.Gui.Chat.Print(string.Format(Loc.Localize("DalamudWelcome", "Dalamud vD{0} loaded."), assemblyVersion)
+ string.Format(Loc.Localize("PluginsWelcome", " {0} plugin(s) loaded."), this.dalamud.PluginManager.Plugins.Count));
+ string.Format(Loc.Localize("PluginsWelcome", " {0} plugin(s) loaded."), this.dalamud.PluginManager.InstalledPlugins.Count));
if (this.dalamud.Configuration.PrintPluginsWelcomeMsg)
{
foreach (var plugin in this.dalamud.PluginManager.Plugins.OrderBy(x => x.Plugin.Name))
foreach (var plugin in this.dalamud.PluginManager.InstalledPlugins.OrderBy(plugin => plugin.Name))
{
this.dalamud.Framework.Gui.Chat.Print(string.Format(Loc.Localize("DalamudPluginLoaded", " 》 {0} v{1} loaded."), plugin.Plugin.Name, plugin.Definition.AssemblyVersion));
this.dalamud.Framework.Gui.Chat.Print(string.Format(Loc.Localize("DalamudPluginLoaded", " 》 {0} v{1} loaded."), plugin.Name, plugin.Manifest.AssemblyVersion));
}
}
@ -261,16 +261,15 @@ namespace Dalamud.Game
Type = XivChatType.Notice,
});
if (DalamudChangelogWindow.WarrantsChangelog)
#pragma warning disable CS0162 // Unreachable code detected
this.dalamud.DalamudUi.OpenChangelog();
#pragma warning restore CS0162 // Unreachable code detected
if (this.dalamud.DalamudUi.WarrantsChangelog)
this.dalamud.DalamudUi.OpenChangelogWindow();
this.dalamud.Configuration.LastVersion = assemblyVersion;
this.dalamud.Configuration.Save();
}
Task.Run(() => this.dalamud.PluginRepository.UpdatePlugins(!this.dalamud.Configuration.AutoUpdatePlugins)).ContinueWith(t =>
Task.Run(() => this.dalamud.PluginManager.UpdatePlugins(!this.dalamud.Configuration.AutoUpdatePlugins))
.ContinueWith(t =>
{
if (t.IsFaulted)
{
@ -278,13 +277,13 @@ namespace Dalamud.Game
}
else
{
var updatedPlugins = t.Result.UpdatedPlugins;
var updatedPlugins = t.Result;
if (updatedPlugins != null && updatedPlugins.Any())
{
if (this.dalamud.Configuration.AutoUpdatePlugins)
{
this.dalamud.PluginRepository.PrintUpdatedPlugins(updatedPlugins, Loc.Localize("DalamudPluginAutoUpdate", "Auto-update:"));
this.dalamud.PluginManager.PrintUpdatedPlugins(updatedPlugins, Loc.Localize("DalamudPluginAutoUpdate", "Auto-update:"));
}
else
{

View file

@ -34,14 +34,14 @@ namespace Dalamud.Game.ClientState.Actors
/// </summary>
/// <param name="dalamud">The Dalamud instance.</param>
/// <param name="addressResolver">The ClientStateAddressResolver instance.</param>
public ActorTable(Dalamud dalamud, ClientStateAddressResolver addressResolver)
internal ActorTable(Dalamud dalamud, ClientStateAddressResolver addressResolver)
{
this.address = addressResolver;
this.dalamud = dalamud;
dalamud.Framework.OnUpdateEvent += this.Framework_OnUpdateEvent;
Log.Verbose("Actor table address {ActorTable}", this.address.ActorTable);
Log.Verbose($"Actor table address 0x{this.address.ActorTable.ToInt64():X}");
}
/// <summary>

View file

@ -11,7 +11,7 @@ namespace Dalamud.Game.ClientState.Actors.Resolvers
/// Initializes a new instance of the <see cref="BaseResolver"/> class.
/// </summary>
/// <param name="dalamud">The Dalamud instance.</param>
public BaseResolver(Dalamud dalamud)
internal BaseResolver(Dalamud dalamud)
{
this.dalamud = dalamud;
}
@ -19,6 +19,6 @@ namespace Dalamud.Game.ClientState.Actors.Resolvers
/// <summary>
/// Gets the Dalamud instance.
/// </summary>
protected Dalamud Dalamud => this.dalamud;
internal Dalamud Dalamud => this.dalamud;
}
}

View file

@ -16,7 +16,7 @@ namespace Dalamud.Game.ClientState.Actors.Resolvers
/// </summary>
/// <param name="id">The ID of the classJob.</param>
/// <param name="dalamud">The Dalamud instance.</param>
public ClassJob(byte id, Dalamud dalamud)
internal ClassJob(byte id, Dalamud dalamud)
: base(dalamud)
{
this.Id = id;

View file

@ -16,7 +16,7 @@ namespace Dalamud.Game.ClientState.Actors.Resolvers
/// </summary>
/// <param name="id">The ID of the world.</param>
/// <param name="dalamud">The Dalamud instance.</param>
public World(ushort id, Dalamud dalamud)
internal World(ushort id, Dalamud dalamud)
: base(dalamud)
{
this.Id = id;

View file

@ -1,7 +1,6 @@
using System;
using System.Text;
using Dalamud.Game.ClientState.Structs;
using Serilog;
namespace Dalamud.Game.ClientState.Actors.Types
{
@ -22,7 +21,7 @@ namespace Dalamud.Game.ClientState.Actors.Types
/// <param name="actorStruct">The memory representation of the base actor.</param>
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
/// <param name="address">The address of this actor in memory.</param>
public Actor(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
internal Actor(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
{
this.actorStruct = actorStruct;
this.dalamud = dalamud;
@ -94,7 +93,7 @@ namespace Dalamud.Game.ClientState.Actors.Types
/// <summary>
/// Gets the <see cref="Dalamud"/> backing instance.
/// </summary>
protected Dalamud Dalamud => this.dalamud;
internal Dalamud Dalamud => this.dalamud;
/// <inheritdoc/>
bool IEquatable<Actor>.Equals(Actor other) => this.ActorId == other.ActorId;

View file

@ -16,7 +16,7 @@ namespace Dalamud.Game.ClientState.Actors.Types
/// <param name="actorStruct">The memory representation of the base actor.</param>
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
/// <param name="address">The address of this actor in memory.</param>
protected Chara(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
internal Chara(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
: base(address, actorStruct, dalamud)
{
}

View file

@ -14,7 +14,7 @@ namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer
/// <param name="actorStruct">The memory representation of the base actor.</param>
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
/// <param name="address">The address of this actor in memory.</param>
public BattleNpc(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
internal BattleNpc(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
: base(address, actorStruct, dalamud)
{
}

View file

@ -14,7 +14,7 @@ namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer
/// <param name="actorStruct">The memory representation of the base actor.</param>
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
/// <param name="address">The address of this actor in memory.</param>
public EventObj(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
internal EventObj(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
: base(address, actorStruct, dalamud)
{
}

View file

@ -14,7 +14,7 @@ namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer
/// <param name="actorStruct">The memory representation of the base actor.</param>
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
/// <param name="address">The address of this actor in memory.</param>
public Npc(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
internal Npc(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
: base(address, actorStruct, dalamud)
{
}

View file

@ -20,7 +20,7 @@ namespace Dalamud.Game.ClientState.Actors.Types
/// <param name="actorStruct">The memory representation of the base actor.</param>
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
/// <param name="address">The address of this actor in memory.</param>
public PlayerCharacter(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
internal PlayerCharacter(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
: base(address, actorStruct, dalamud)
{
var companyTagBytes = new byte[5];

View file

@ -15,7 +15,7 @@ namespace Dalamud.Game.ClientState
/// <summary>
/// This class represents the state of the game client at the time of access.
/// </summary>
public class ClientState : INotifyPropertyChanged, IDisposable
public sealed class ClientState : INotifyPropertyChanged, IDisposable
{
/// <summary>
/// The table of all present actors.
@ -80,7 +80,7 @@ namespace Dalamud.Game.ClientState
/// <param name="dalamud">Dalamud instance.</param>
/// <param name="startInfo">StartInfo of the current Dalamud launch.</param>
/// <param name="scanner">Sig scanner.</param>
public ClientState(Dalamud dalamud, DalamudStartInfo startInfo, SigScanner scanner)
internal ClientState(Dalamud dalamud, DalamudStartInfo startInfo, SigScanner scanner)
{
this.dalamud = dalamud;
this.address = new ClientStateAddressResolver();
@ -104,7 +104,7 @@ namespace Dalamud.Game.ClientState
this.Targets = new Targets(dalamud, this.address);
Log.Verbose("SetupTerritoryType address {SetupTerritoryType}", this.address.SetupTerritoryType);
Log.Verbose($"SetupTerritoryType address 0x{this.address.SetupTerritoryType.ToInt64():X}");
this.setupTerritoryTypeHook = new Hook<SetupTerritoryTypeDelegate>(this.address.SetupTerritoryType, this.SetupTerritoryTypeDetour);

View file

@ -1,4 +1,4 @@
using System;
using System;
using Dalamud.Game.ClientState.Structs;
using Dalamud.Hooking;
@ -12,7 +12,7 @@ namespace Dalamud.Game.ClientState
///
/// Will block game's gamepad input if <see cref="ImGuiConfigFlags.NavEnableGamepad"/> is set.
/// </summary>
public unsafe class GamepadState
public unsafe class GamepadState : IDisposable
{
private readonly Hook<ControllerPoll> gamepadPoll;
@ -29,12 +29,8 @@ namespace Dalamud.Game.ClientState
/// <param name="resolver">Resolver knowing the pointer to the GamepadPoll function.</param>
public GamepadState(ClientStateAddressResolver resolver)
{
#if DEBUG
Log.Verbose("GamepadPoll address {GamepadPoll}", resolver.GamepadPoll);
#endif
this.gamepadPoll = new Hook<ControllerPoll>(
resolver.GamepadPoll,
(ControllerPoll)this.GamepadPollDetour);
Log.Verbose($"GamepadPoll address 0x{resolver.GamepadPoll.ToInt64():X}");
this.gamepadPoll = new Hook<ControllerPoll>(resolver.GamepadPoll, this.GamepadPollDetour);
}
/// <summary>

View file

@ -17,7 +17,7 @@ namespace Dalamud.Game.ClientState
{
this.Address = addressResolver;
Log.Verbose("JobGaugeData address {JobGaugeData}", this.Address.JobGaugeData);
Log.Verbose($"JobGaugeData address 0x{this.Address.JobGaugeData.ToInt64():X}");
}
private ClientStateAddressResolver Address { get; }

View file

@ -25,7 +25,7 @@ namespace Dalamud.Game.ClientState
{
this.bufferBase = moduleBaseAddress + Marshal.ReadInt32(addressResolver.KeyboardState);
Log.Verbose($"Keyboard state buffer address {this.bufferBase}");
Log.Verbose($"Keyboard state buffer address 0x{this.bufferBase.ToInt64():X}");
}
/// <summary>

View file

@ -25,7 +25,7 @@ namespace Dalamud.Game.ClientState
/// </summary>
/// <param name="dalamud">The Dalamud instance.</param>
/// <param name="addressResolver">The ClientStateAddressResolver instance.</param>
public PartyList(Dalamud dalamud, ClientStateAddressResolver addressResolver)
internal PartyList(Dalamud dalamud, ClientStateAddressResolver addressResolver)
{
this.address = addressResolver;
this.dalamud = dalamud;

View file

@ -32,6 +32,7 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
/// <summary>
/// Gets the next step in the current dance.
/// </summary>
/// <returns>The next dance step action ID.</returns>
public ulong NextStep() => (ulong)(15999 + this.stepOrder[this.NumCompleteSteps] - 1);
/// <summary>

View file

@ -27,7 +27,7 @@ namespace Dalamud.Game.Command
/// </summary>
/// <param name="dalamud">The Dalamud instance.</param>
/// <param name="language">The client language requested.</param>
public CommandManager(Dalamud dalamud, ClientLanguage language)
internal CommandManager(Dalamud dalamud, ClientLanguage language)
{
this.dalamud = dalamud;
@ -128,7 +128,8 @@ namespace Dalamud.Game.Command
/// <returns>If adding was successful.</returns>
public bool AddHandler(string command, CommandInfo info)
{
if (info == null) throw new ArgumentNullException(nameof(info), "Command handler is null.");
if (info == null)
throw new ArgumentNullException(nameof(info), "Command handler is null.");
try
{

383
Dalamud/Game/GameVersion.cs Normal file
View file

@ -0,0 +1,383 @@
using System;
using System.Globalization;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
namespace Dalamud.Game
{
/// <summary>
/// A GameVersion object contains give hierarchical numeric components: year, month,
/// day, major and minor. All components may be unspecified, which is represented
/// internally as a -1. By definition, an unspecified component matches anything
/// (both unspecified and specified), and an unspecified component is "less than" any
/// specified component. It will also equal the string "any" if all components are
/// unspecified. The value can be retrieved from the ffxivgame.ver file in your game
/// installation directory.
/// </summary>
[Serializable]
public sealed class GameVersion : ICloneable, IComparable, IComparable<GameVersion>, IEquatable<GameVersion>
{
private static readonly GameVersion AnyVersion = new();
/// <summary>
/// Initializes a new instance of the <see cref="GameVersion"/> class.
/// </summary>
/// <param name="version">Version string to parse.</param>
[JsonConstructor]
public GameVersion(string version)
{
var ver = Parse(version);
this.Year = ver.Year;
this.Month = ver.Month;
this.Day = ver.Day;
this.Major = ver.Major;
this.Minor = ver.Minor;
}
/// <summary>
/// Initializes a new instance of the <see cref="GameVersion"/> class.
/// </summary>
/// <param name="year">The year.</param>
/// <param name="month">The month.</param>
/// <param name="day">The day.</param>
/// <param name="major">The major version.</param>
/// <param name="minor">The minor version.</param>
public GameVersion(int year, int month, int day, int major, int minor)
{
if ((this.Year = year) < 0)
throw new ArgumentOutOfRangeException(nameof(year));
if ((this.Month = month) < 0)
throw new ArgumentOutOfRangeException(nameof(month));
if ((this.Day = day) < 0)
throw new ArgumentOutOfRangeException(nameof(day));
if ((this.Major = major) < 0)
throw new ArgumentOutOfRangeException(nameof(major));
if ((this.Minor = minor) < 0)
throw new ArgumentOutOfRangeException(nameof(minor));
}
/// <summary>
/// Initializes a new instance of the <see cref="GameVersion"/> class.
/// </summary>
/// <param name="year">The year.</param>
/// <param name="month">The month.</param>
/// <param name="day">The day.</param>
/// <param name="major">The major version.</param>
public GameVersion(int year, int month, int day, int major)
{
if ((this.Year = year) < 0)
throw new ArgumentOutOfRangeException(nameof(year));
if ((this.Month = month) < 0)
throw new ArgumentOutOfRangeException(nameof(month));
if ((this.Day = day) < 0)
throw new ArgumentOutOfRangeException(nameof(day));
if ((this.Major = major) < 0)
throw new ArgumentOutOfRangeException(nameof(major));
}
/// <summary>
/// Initializes a new instance of the <see cref="GameVersion"/> class.
/// </summary>
/// <param name="year">The year.</param>
/// <param name="month">The month.</param>
/// <param name="day">The day.</param>
public GameVersion(int year, int month, int day)
{
if ((this.Year = year) < 0)
throw new ArgumentOutOfRangeException(nameof(year));
if ((this.Month = month) < 0)
throw new ArgumentOutOfRangeException(nameof(month));
if ((this.Day = day) < 0)
throw new ArgumentOutOfRangeException(nameof(day));
}
/// <summary>
/// Initializes a new instance of the <see cref="GameVersion"/> class.
/// </summary>
/// <param name="year">The year.</param>
/// <param name="month">The month.</param>
public GameVersion(int year, int month)
{
if ((this.Year = year) < 0)
throw new ArgumentOutOfRangeException(nameof(year));
if ((this.Month = month) < 0)
throw new ArgumentOutOfRangeException(nameof(month));
}
/// <summary>
/// Initializes a new instance of the <see cref="GameVersion"/> class.
/// </summary>
/// <param name="year">The year.</param>
public GameVersion(int year)
{
if ((this.Year = year) < 0)
throw new ArgumentOutOfRangeException(nameof(year));
}
/// <summary>
/// Initializes a new instance of the <see cref="GameVersion"/> class.
/// </summary>
public GameVersion()
{
}
/// <summary>
/// Gets the default "any" game version.
/// </summary>
public static GameVersion Any => AnyVersion;
/// <summary>
/// Gets the year component.
/// </summary>
public int Year { get; } = -1;
/// <summary>
/// Gets the month component.
/// </summary>
public int Month { get; } = -1;
/// <summary>
/// Gets the day component.
/// </summary>
public int Day { get; } = -1;
/// <summary>
/// Gets the major version component.
/// </summary>
public int Major { get; } = -1;
/// <summary>
/// Gets the minor version component.
/// </summary>
public int Minor { get; } = -1;
public static implicit operator GameVersion(string ver)
{
return Parse(ver);
}
public static bool operator ==(GameVersion v1, GameVersion v2)
{
if (v1 is null)
{
return v2 is null;
}
return v1.Equals(v2);
}
public static bool operator !=(GameVersion v1, GameVersion v2)
{
return !(v1 == v2);
}
public static bool operator <(GameVersion v1, GameVersion v2)
{
if (v1 is null)
throw new ArgumentNullException(nameof(v1));
return v1.CompareTo(v2) < 0;
}
public static bool operator <=(GameVersion v1, GameVersion v2)
{
if (v1 is null)
throw new ArgumentNullException(nameof(v1));
return v1.CompareTo(v2) <= 0;
}
public static bool operator >(GameVersion v1, GameVersion v2)
{
return v2 < v1;
}
public static bool operator >=(GameVersion v1, GameVersion v2)
{
return v2 <= v1;
}
/// <summary>
/// Parse a version string. YYYY.MM.DD.majr.minr or "any".
/// </summary>
/// <param name="input">Input to parse.</param>
/// <returns>GameVersion object.</returns>
public static GameVersion Parse(string input)
{
if (input == null)
throw new ArgumentNullException(nameof(input));
if (input.ToLower(CultureInfo.InvariantCulture) == "any")
return new GameVersion();
var parts = input.Split('.');
var tplParts = parts.Select(p =>
{
var result = int.TryParse(p, out var value);
return (result, value);
}).ToArray();
if (tplParts.Any(t => !t.result))
throw new FormatException("Bad formatting");
var intParts = tplParts.Select(t => t.value).ToArray();
var len = intParts.Length;
if (len == 1)
return new GameVersion(intParts[0]);
else if (len == 2)
return new GameVersion(intParts[0], intParts[1]);
else if (len == 3)
return new GameVersion(intParts[0], intParts[1], intParts[2]);
else if (len == 4)
return new GameVersion(intParts[0], intParts[1], intParts[2], intParts[3]);
else if (len == 5)
return new GameVersion(intParts[0], intParts[1], intParts[2], intParts[3], intParts[4]);
else
throw new ArgumentException("Too many parts");
}
/// <summary>
/// Try to parse a version string. YYYY.MM.DD.majr.minr or "any".
/// </summary>
/// <param name="input">Input to parse.</param>
/// <param name="result">GameVersion object.</param>
/// <returns>Success or failure.</returns>
public static bool TryParse(string input, out GameVersion result)
{
try
{
result = Parse(input);
return true;
}
catch
{
result = null;
return false;
}
}
/// <inheritdoc/>
public object Clone() => new GameVersion(this.Year, this.Month, this.Day, this.Major, this.Minor);
/// <inheritdoc/>
public int CompareTo(object obj)
{
if (obj == null)
return 1;
if (obj is GameVersion value)
{
return this.CompareTo(value);
}
else
{
throw new ArgumentException("Argument must be a GameVersion");
}
}
/// <inheritdoc/>
public int CompareTo(GameVersion value)
{
if (value == null)
return 1;
if (this == value)
return 0;
if (this == AnyVersion)
return 1;
if (value == AnyVersion)
return -1;
if (this.Year != value.Year)
return this.Year > value.Year ? 1 : -1;
if (this.Month != value.Month)
return this.Month > value.Month ? 1 : -1;
if (this.Day != value.Day)
return this.Day > value.Day ? 1 : -1;
if (this.Major != value.Major)
return this.Major > value.Major ? 1 : -1;
if (this.Minor != value.Minor)
return this.Minor > value.Minor ? 1 : -1;
return 0;
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is not GameVersion value)
return false;
return this.Equals(value);
}
/// <inheritdoc/>
public bool Equals(GameVersion value)
{
if (value == null)
{
return false;
}
return
(this.Year == value.Year) &&
(this.Month == value.Month) &&
(this.Day == value.Day) &&
(this.Major == value.Major) &&
(this.Minor == value.Minor);
}
/// <inheritdoc/>
public override int GetHashCode()
{
var accumulator = 0;
// This might be horribly wrong, but it isn't used heavily.
accumulator |= this.Year.GetHashCode();
accumulator |= this.Month.GetHashCode();
accumulator |= this.Day.GetHashCode();
accumulator |= this.Major.GetHashCode();
accumulator |= this.Minor.GetHashCode();
return accumulator;
}
/// <inheritdoc/>
public override string ToString()
{
if (this.Year == -1 &&
this.Month == -1 &&
this.Day == -1 &&
this.Major == -1 &&
this.Minor == -1)
return "any";
return new StringBuilder()
.Append(string.Format("{0:D4}.", this.Year == -1 ? 0 : this.Year))
.Append(string.Format("{0:D2}.", this.Month == -1 ? 0 : this.Month))
.Append(string.Format("{0:D2}.", this.Day == -1 ? 0 : this.Day))
.Append(string.Format("{0:D4}.", this.Major == -1 ? 0 : this.Major))
.Append(string.Format("{0:D4}", this.Minor == -1 ? 0 : this.Minor))
.ToString();
}
}
}

View file

@ -0,0 +1,80 @@
using System;
using Newtonsoft.Json;
namespace Dalamud.Game
{
/// <summary>
/// Converts a <see cref="GameVersion"/> to and from a string (e.g. <c>"2010.01.01.1234.5678"</c>).
/// </summary>
public sealed class GameVersionConverter : JsonConverter
{
/// <summary>
/// Writes the JSON representation of the object.
/// </summary>
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
if (value == null)
{
writer.WriteNull();
}
else if (value is GameVersion)
{
writer.WriteValue(value.ToString());
}
else
{
throw new JsonSerializationException("Expected GameVersion object value");
}
}
/// <summary>
/// Reads the JSON representation of the object.
/// </summary>
/// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
/// <param name="objectType">Type of the object.</param>
/// <param name="existingValue">The existing property value of the JSON that is being converted.</param>
/// <param name="serializer">The calling serializer.</param>
/// <returns>The object value.</returns>
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
else
{
if (reader.TokenType == JsonToken.String)
{
try
{
return new GameVersion((string)reader.Value!);
}
catch (Exception ex)
{
throw new JsonSerializationException($"Error parsing GameVersion string: {reader.Value}", ex);
}
}
else
{
throw new JsonSerializationException($"Unexpected token or value when parsing GameVersion. Token: {reader.TokenType}, Value: {reader.Value}");
}
}
}
/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert(Type objectType)
{
return objectType == typeof(GameVersion);
}
}
}

View file

@ -29,7 +29,7 @@ namespace Dalamud.Game.Internal
this.debugCheckAddress = IntPtr.Zero;
}
Log.Verbose("DebugCheck address {DebugCheckAddress}", this.debugCheckAddress);
Log.Verbose($"Debug check address 0x{this.debugCheckAddress.ToInt64():X}");
}
/// <summary>
@ -45,7 +45,7 @@ namespace Dalamud.Game.Internal
this.original = new byte[this.nop.Length];
if (this.debugCheckAddress != IntPtr.Zero && !this.IsEnabled)
{
Log.Information($"Overwriting debug check @ 0x{this.debugCheckAddress.ToInt64():X}");
Log.Information($"Overwriting debug check at 0x{this.debugCheckAddress.ToInt64():X}");
SafeMemory.ReadBytes(this.debugCheckAddress, this.nop.Length, out this.original);
SafeMemory.WriteBytes(this.debugCheckAddress, this.nop);
}
@ -64,7 +64,7 @@ namespace Dalamud.Game.Internal
{
if (this.debugCheckAddress != IntPtr.Zero && this.original != null)
{
Log.Information($"Reverting debug check @ 0x{this.debugCheckAddress.ToInt64():X}");
Log.Information($"Reverting debug check at 0x{this.debugCheckAddress.ToInt64():X}");
SafeMemory.WriteBytes(this.debugCheckAddress, this.original);
}
else
@ -110,9 +110,11 @@ namespace Dalamud.Game.Internal
{
// If anti-debug is enabled and is being disposed, odds are either the game is exiting, or Dalamud is being reloaded.
// If it is the latter, there's half a chance a debugger is currently attached. There's no real need to disable the
// check in either situation anyways.
// this.Disable();
// check in either situation anyways. However if Dalamud is being reloaded, the sig may fail so may as well undo it.
this.Disable();
}
this.disposed = true;
}
}
}

View file

@ -0,0 +1,227 @@
namespace Dalamud.Game.Internal.DXGI.Definitions
{
/// <summary>
/// Contains a full list of ID3D11Device functions to be used as an indexer into the DirectX Virtual Function Table entries.
/// </summary>
internal enum ID3D11DeviceVtbl
{
// IUnknown
/// <summary>
/// IUnknown::QueryInterface method (unknwn.h).
/// </summary>
QueryInterface = 0,
/// <summary>
/// IUnknown::AddRef method (unknwn.h).
/// </summary>
AddRef = 1,
/// <summary>
/// IUnknown::Release method (unknwn.h).
/// </summary>
Release = 2,
// ID3D11Device
/// <summary>
/// ID3D11Device::CreateBuffer method (d3d11.h).
/// </summary>
CreateBuffer = 3,
/// <summary>
/// ID3D11Device::CreateTexture1D method (d3d11.h).
/// </summary>
CreateTexture1D = 4,
/// <summary>
/// ID3D11Device::CreateTexture2D method (d3d11.h).
/// </summary>
CreateTexture2D = 5,
/// <summary>
/// ID3D11Device::CreateTexture3D method (d3d11.h).
/// </summary>
CreateTexture3D = 6,
/// <summary>
/// ID3D11Device::CreateShaderResourceView method (d3d11.h).
/// </summary>
CreateShaderResourceView = 7,
/// <summary>
/// ID3D11Device::CreateUnorderedAccessView method (d3d11.h).
/// </summary>
CreateUnorderedAccessView = 8,
/// <summary>
/// ID3D11Device::CreateRenderTargetView method (d3d11.h).
/// </summary>
CreateRenderTargetView = 9,
/// <summary>
/// ID3D11Device::CreateDepthStencilView method (d3d11.h).
/// </summary>
CreateDepthStencilView = 10,
/// <summary>
/// ID3D11Device::CreateInputLayout method (d3d11.h).
/// </summary>
CreateInputLayout = 11,
/// <summary>
/// ID3D11Device::CreateVertexShader method (d3d11.h).
/// </summary>
CreateVertexShader = 12,
/// <summary>
/// ID3D11Device::CreateGeometryShader method (d3d11.h).
/// </summary>
CreateGeometryShader = 13,
/// <summary>
/// ID3D11Device::CreateGeometryShaderWithStreamOutput method (d3d11.h).
/// </summary>
CreateGeometryShaderWithStreamOutput = 14,
/// <summary>
/// ID3D11Device::CreatePixelShader method (d3d11.h).
/// </summary>
CreatePixelShader = 15,
/// <summary>
/// ID3D11Device::CreateHullShader method (d3d11.h).
/// </summary>
CreateHullShader = 16,
/// <summary>
/// ID3D11Device::CreateDomainShader method (d3d11.h).
/// </summary>
CreateDomainShader = 17,
/// <summary>
/// ID3D11Device::CreateComputeShader method (d3d11.h).
/// </summary>
CreateComputeShader = 18,
/// <summary>
/// ID3D11Device::CreateClassLinkage method (d3d11.h).
/// </summary>
CreateClassLinkage = 19,
/// <summary>
/// ID3D11Device::CreateBlendState method (d3d11.h).
/// </summary>
CreateBlendState = 20,
/// <summary>
/// ID3D11Device::CreateDepthStencilState method (d3d11.h).
/// </summary>
CreateDepthStencilState = 21,
/// <summary>
/// ID3D11Device::CreateRasterizerState method (d3d11.h).
/// </summary>
CreateRasterizerState = 22,
/// <summary>
/// ID3D11Device::CreateSamplerState method (d3d11.h).
/// </summary>
CreateSamplerState = 23,
/// <summary>
/// ID3D11Device::CreateQuery method (d3d11.h).
/// </summary>
CreateQuery = 24,
/// <summary>
/// ID3D11Device::CreatePredicate method (d3d11.h).
/// </summary>
CreatePredicate = 25,
/// <summary>
/// ID3D11Device::CreateCounter method (d3d11.h).
/// </summary>
CreateCounter = 26,
/// <summary>
/// ID3D11Device::CreateDeferredContext method (d3d11.h).
/// </summary>
CreateDeferredContext = 27,
/// <summary>
/// ID3D11Device::OpenSharedResource method (d3d11.h).
/// </summary>
OpenSharedResource = 28,
/// <summary>
/// ID3D11Device::CheckFormatSupport method (d3d11.h).
/// </summary>
CheckFormatSupport = 29,
/// <summary>
/// ID3D11Device::CheckMultisampleQualityLevels method (d3d11.h).
/// </summary>
CheckMultisampleQualityLevels = 30,
/// <summary>
/// ID3D11Device::CheckCounterInfo method (d3d11.h).
/// </summary>
CheckCounterInfo = 31,
/// <summary>
/// ID3D11Device::CheckCounter method (d3d11.h).
/// </summary>
CheckCounter = 32,
/// <summary>
/// ID3D11Device::CheckFeatureSupport method (d3d11.h).
/// </summary>
CheckFeatureSupport = 33,
/// <summary>
/// ID3D11Device::GetPrivateData method (d3d11.h).
/// </summary>
GetPrivateData = 34,
/// <summary>
/// ID3D11Device::SetPrivateData method (d3d11.h).
/// </summary>
SetPrivateData = 35,
/// <summary>
/// ID3D11Device::SetPrivateDataInterface method (d3d11.h).
/// </summary>
SetPrivateDataInterface = 36,
/// <summary>
/// ID3D11Device::GetFeatureLevel method (d3d11.h).
/// </summary>
GetFeatureLevel = 37,
/// <summary>
/// ID3D11Device::GetCreationFlags method (d3d11.h).
/// </summary>
GetCreationFlags = 38,
/// <summary>
/// ID3D11Device::GetDeviceRemovedReason method (d3d11.h).
/// </summary>
GetDeviceRemovedReason = 39,
/// <summary>
/// ID3D11Device::GetImmediateContext method (d3d11.h).
/// </summary>
GetImmediateContext = 40,
/// <summary>
/// ID3D11Device::SetExceptionMode method (d3d11.h).
/// </summary>
SetExceptionMode = 41,
/// <summary>
/// ID3D11Device::GetExceptionMode method (d3d11.h).
/// </summary>
GetExceptionMode = 42,
}
}

View file

@ -0,0 +1,107 @@
namespace Dalamud.Game.Internal.DXGI.Definitions
{
/// <summary>
/// Contains a full list of IDXGISwapChain functions to be used as an indexer into the SwapChain Virtual Function Table
/// entries.
/// </summary>
internal enum IDXGISwapChainVtbl
{
// IUnknown
/// <summary>
/// IUnknown::QueryInterface method (unknwn.h).
/// </summary>
QueryInterface = 0,
/// <summary>
/// IUnknown::AddRef method (unknwn.h).
/// </summary>
AddRef = 1,
/// <summary>
/// IUnknown::Release method (unknwn.h).
/// </summary>
Release = 2,
// IDXGIObject
/// <summary>
/// IDXGIObject::SetPrivateData method (dxgi.h).
/// </summary>
SetPrivateData = 3,
/// <summary>
/// IDXGIObject::SetPrivateDataInterface method (dxgi.h).
/// </summary>
SetPrivateDataInterface = 4,
/// <summary>
/// IDXGIObject::GetPrivateData method (dxgi.h).
/// </summary>
GetPrivateData = 5,
/// <summary>
/// IDXGIObject::GetParent method (dxgi.h).
/// </summary>
GetParent = 6,
// IDXGIDeviceSubObject
/// <summary>
/// IDXGIDeviceSubObject::GetDevice method (dxgi.h).
/// </summary>
GetDevice = 7,
// IDXGISwapChain
/// <summary>
/// IDXGISwapChain::Present method (dxgi.h).
/// </summary>
Present = 8,
/// <summary>
/// IUnknIDXGISwapChainown::GetBuffer method (dxgi.h).
/// </summary>
GetBuffer = 9,
/// <summary>
/// IDXGISwapChain::SetFullscreenState method (dxgi.h).
/// </summary>
SetFullscreenState = 10,
/// <summary>
/// IDXGISwapChain::GetFullscreenState method (dxgi.h).
/// </summary>
GetFullscreenState = 11,
/// <summary>
/// IDXGISwapChain::GetDesc method (dxgi.h).
/// </summary>
GetDesc = 12,
/// <summary>
/// IDXGISwapChain::ResizeBuffers method (dxgi.h).
/// </summary>
ResizeBuffers = 13,
/// <summary>
/// IDXGISwapChain::ResizeTarget method (dxgi.h).
/// </summary>
ResizeTarget = 14,
/// <summary>
/// IDXGISwapChain::GetContainingOutput method (dxgi.h).
/// </summary>
GetContainingOutput = 15,
/// <summary>
/// IDXGISwapChain::GetFrameStatistics method (dxgi.h).
/// </summary>
GetFrameStatistics = 16,
/// <summary>
/// IDXGISwapChain::GetLastPresentCount method (dxgi.h).
/// </summary>
GetLastPresentCount = 17,
}
}

View file

@ -22,7 +22,7 @@ namespace Dalamud.Game.Internal.DXGI
{
var module = Process.GetCurrentProcess().Modules.Cast<ProcessModule>().First(m => m.ModuleName == "dxgi.dll");
Log.Debug($"Found DXGI: {module.BaseAddress.ToInt64():X}");
Log.Debug($"Found DXGI: 0x{module.BaseAddress.ToInt64():X}");
var scanner = new SigScanner(module);

View file

@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Dalamud.Game.Internal.DXGI.Definitions;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
@ -19,9 +19,6 @@ namespace Dalamud.Game.Internal.DXGI
/// </remarks>
public class SwapChainVtableResolver : BaseAddressResolver, ISwapChainAddressResolver
{
private const int DxgiSwapchainMethodCount = 18;
private const int D3D11DeviceMethodCount = 43;
private List<IntPtr> d3d11VTblAddresses;
private List<IntPtr> dxgiSwapChainVTblAddresses;
@ -34,30 +31,33 @@ namespace Dalamud.Game.Internal.DXGI
/// <inheritdoc/>
protected override void Setup64Bit(SigScanner sig)
{
// Create temporary device + swapchain and determine method addresses
if (this.d3d11VTblAddresses == null)
{
// Create temporary device + swapchain and determine method addresses
var renderForm = new Form();
// A renderable object isnt required, just a handle
var handle = Marshal.AllocHGlobal(Marshal.SizeOf<IntPtr>());
Device.CreateWithSwapChain(
DriverType.Hardware,
DeviceCreationFlags.BgraSupport,
CreateSwapChainDescription(renderForm.Handle),
CreateSwapChainDescription(handle),
out var device,
out var swapChain);
if (device != null && swapChain != null)
{
this.d3d11VTblAddresses = this.GetVTblAddresses(device.NativePointer, D3D11DeviceMethodCount);
this.dxgiSwapChainVTblAddresses = this.GetVTblAddresses(swapChain.NativePointer, DxgiSwapchainMethodCount);
this.d3d11VTblAddresses = GetVTblAddresses(device.NativePointer, Enum.GetValues(typeof(ID3D11DeviceVtbl)).Length);
this.dxgiSwapChainVTblAddresses = GetVTblAddresses(swapChain.NativePointer, Enum.GetValues(typeof(IDXGISwapChainVtbl)).Length);
}
device?.Dispose();
swapChain?.Dispose();
Marshal.FreeHGlobal(handle);
}
this.Present = this.dxgiSwapChainVTblAddresses[8];
this.ResizeBuffers = this.dxgiSwapChainVTblAddresses[13];
this.Present = this.dxgiSwapChainVTblAddresses[(int)IDXGISwapChainVtbl.Present];
this.ResizeBuffers = this.dxgiSwapChainVTblAddresses[(int)IDXGISwapChainVtbl.ResizeBuffers];
}
private static SwapChainDescription CreateSwapChainDescription(IntPtr renderForm)
@ -75,12 +75,12 @@ namespace Dalamud.Game.Internal.DXGI
};
}
private List<IntPtr> GetVTblAddresses(IntPtr pointer, int numberOfMethods)
private static List<IntPtr> GetVTblAddresses(IntPtr pointer, int numberOfMethods)
{
return this.GetVTblAddresses(pointer, 0, numberOfMethods);
return GetVTblAddresses(pointer, 0, numberOfMethods);
}
private List<IntPtr> GetVTblAddresses(IntPtr pointer, int startIndex, int numberOfMethods)
private static List<IntPtr> GetVTblAddresses(IntPtr pointer, int startIndex, int numberOfMethods)
{
var vtblAddresses = new List<IntPtr>();
var vTable = Marshal.ReadIntPtr(pointer);

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Dalamud.Game.Internal.Gui;
using Dalamud.Game.Internal.Libc;
@ -29,13 +30,13 @@ namespace Dalamud.Game.Internal
/// </summary>
/// <param name="scanner">The SigScanner instance.</param>
/// <param name="dalamud">The Dalamud instance.</param>
public Framework(SigScanner scanner, Dalamud dalamud)
internal Framework(SigScanner scanner, Dalamud dalamud)
{
this.dalamud = dalamud;
this.Address = new FrameworkAddressResolver();
this.Address.Setup(scanner);
Log.Verbose("Framework address {FrameworkAddress}", this.Address.BaseAddress);
Log.Verbose($"Framework address 0x{this.Address.BaseAddress.ToInt64():X}");
if (this.Address.BaseAddress == IntPtr.Zero)
{
throw new InvalidOperationException("Framework is not initalized yet.");
@ -143,6 +144,11 @@ namespace Dalamud.Game.Internal
this.Gui.Dispose();
this.Network.Dispose();
this.updateHook.Disable();
this.destroyHook.Disable();
this.realDestroyHook.Disable();
Thread.Sleep(500);
this.updateHook.Dispose();
this.destroyHook.Dispose();
this.realDestroyHook.Dispose();

View file

@ -44,7 +44,7 @@ namespace Dalamud.Game.Internal
// 00007FF701AD666A | 48 8D ?? ?? ?? 00 00 | LEA RCX,QWORD PTR DS:[RBX + 2C38]
// 00007FF701AD6671 | E8 ?? ?? ?? ?? | CALL ffxiv_dx11.7FF701E2A7D0
// 00007FF701AD6676 | 48 8D ?? ?? ?? ?? ?? | LEA RAX,QWORD PTR DS:[7FF702C31F80
var fwDtor = scanner.ScanText("48C705????????00000000 E8???????? 488D??????0000 E8???????? 488D");
var fwDtor = scanner.ScanText("48 C7 05 ?? ?? ?? ?? 00 00 00 00 E8 ?? ?? ?? ?? 48 8D ?? ?? ?? 00 00 E8 ?? ?? ?? ?? 48 8D");
var fwOffset = Marshal.ReadInt32(fwDtor + 3);
var pFramework = scanner.ResolveRelativeAddress(fwDtor + 11, fwOffset);

View file

@ -36,14 +36,14 @@ namespace Dalamud.Game.Internal.Gui
/// <param name="baseAddress">The base address of the ChatManager.</param>
/// <param name="scanner">The SigScanner instance.</param>
/// <param name="dalamud">The Dalamud instance.</param>
public ChatGui(IntPtr baseAddress, SigScanner scanner, Dalamud dalamud)
internal ChatGui(IntPtr baseAddress, SigScanner scanner, Dalamud dalamud)
{
this.dalamud = dalamud;
this.address = new ChatGuiAddressResolver(baseAddress);
this.address.Setup(scanner);
Log.Verbose("Chat manager address {ChatManager}", this.address.BaseAddress);
Log.Verbose($"Chat manager address 0x{this.address.BaseAddress.ToInt64():X}");
this.printMessageHook = new Hook<PrintMessageDelegate>(this.address.PrintMessage, this.HandlePrintMessageDetour);
this.populateItemLinkHook = new Hook<PopulateItemLinkDelegate>(this.address.PopulateItemLinkObject, this.HandlePopulateItemLinkDetour);
@ -252,7 +252,7 @@ namespace Dalamud.Game.Internal.Gui
var senderRaw = Encoding.UTF8.GetBytes(chat.Name ?? string.Empty);
using var senderOwned = framework.Libc.NewString(senderRaw);
var messageRaw = chat.MessageBytes ?? new byte[0];
var messageRaw = chat.MessageBytes ?? Array.Empty<byte>();
using var messageOwned = framework.Libc.NewString(messageRaw);
this.HandlePrintMessageDetour(this.baseAddress, chat.Type, senderOwned.Address, messageOwned.Address, chat.SenderId, chat.Parameters);

View file

@ -10,10 +10,10 @@ namespace Dalamud.Game.Internal.Gui
/// <summary>
/// Initializes a new instance of the <see cref="ChatGuiAddressResolver"/> class.
/// </summary>
/// <param name="baseAddres">The base address of the native ChatManager class.</param>
public ChatGuiAddressResolver(IntPtr baseAddres)
/// <param name="baseAddress">The base address of the native ChatManager class.</param>
public ChatGuiAddressResolver(IntPtr baseAddress)
{
this.BaseAddress = baseAddres;
this.BaseAddress = baseAddress;
}
/// <summary>

View file

@ -47,7 +47,7 @@ namespace Dalamud.Game.Internal.Gui
/// <param name="baseAddress">The base address of the native GuiManager class.</param>
/// <param name="scanner">The SigScanner instance.</param>
/// <param name="dalamud">The Dalamud instance.</param>
public GameGui(IntPtr baseAddress, SigScanner scanner, Dalamud dalamud)
internal GameGui(IntPtr baseAddress, SigScanner scanner, Dalamud dalamud)
{
this.dalamud = dalamud;
@ -56,12 +56,12 @@ namespace Dalamud.Game.Internal.Gui
Log.Verbose("===== G A M E G U I =====");
Log.Verbose("GameGuiManager address {Address:X}", this.address.BaseAddress.ToInt64());
Log.Verbose("SetGlobalBgm address {Address:X}", this.address.SetGlobalBgm.ToInt64());
Log.Verbose("HandleItemHover address {Address:X}", this.address.HandleItemHover.ToInt64());
Log.Verbose("HandleItemOut address {Address:X}", this.address.HandleItemOut.ToInt64());
Log.Verbose("GetUIObject address {Address:X}", this.address.GetUIObject.ToInt64());
Log.Verbose("GetAgentModule address {Address:X}", this.address.GetAgentModule.ToInt64());
Log.Verbose($"GameGuiManager address 0x{this.address.BaseAddress.ToInt64():X}");
Log.Verbose($"SetGlobalBgm address 0x{this.address.SetGlobalBgm.ToInt64():X}");
Log.Verbose($"HandleItemHover address 0x{this.address.HandleItemHover.ToInt64():X}");
Log.Verbose($"HandleItemOut address 0x{this.address.HandleItemOut.ToInt64():X}");
Log.Verbose($"GetUIObject address 0x{this.address.GetUIObject.ToInt64():X}");
Log.Verbose($"GetAgentModule address 0x{this.address.GetAgentModule.ToInt64():X}");
this.Chat = new ChatGui(this.address.ChatManager, scanner, dalamud);
this.PartyFinder = new PartyFinderGui(scanner, dalamud);

View file

@ -23,7 +23,7 @@ namespace Dalamud.Game.Internal.Gui
/// </summary>
/// <param name="scanner">The SigScanner instance.</param>
/// <param name="dalamud">The Dalamud instance.</param>
public PartyFinderGui(SigScanner scanner, Dalamud dalamud)
internal PartyFinderGui(SigScanner scanner, Dalamud dalamud)
{
this.dalamud = dalamud;

View file

@ -1,5 +1,7 @@
using System.Runtime.InteropServices;
using FFXIVClientStructs.FFXIV.Component.GUI;
namespace Dalamud.Game.Internal.Gui.Structs
{
/// <summary>

View file

@ -1,130 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace Dalamud.Game.Internal.Gui.Structs
{
/// <summary>
/// Native memory representation of a UI resource node.
/// </summary>
/// <remarks>
/// This is copied from https://github.com/aers/FFXIVClientStructs/blob/main/Component/GUI/AtkResNode.cs.
/// If you need newer features, include FFXIVClientStructs and ILMerge the assembly.
/// </remarks>
[StructLayout(LayoutKind.Explicit, Size = 0xA8)]
public unsafe struct AtkResNode
{
[FieldOffset(0x0)]
public IntPtr AtkEventTarget;
[FieldOffset(0x8)]
public uint NodeID;
[FieldOffset(0x20)]
public AtkResNode* ParentNode;
[FieldOffset(0x28)]
public AtkResNode* PrevSiblingNode;
[FieldOffset(0x30)]
public AtkResNode* NextSiblingNode;
[FieldOffset(0x38)]
public AtkResNode* ChildNode;
[FieldOffset(0x40)]
public ushort Type;
[FieldOffset(0x42)]
public ushort ChildCount;
[FieldOffset(0x44)]
public float X;
[FieldOffset(0x48)]
public float Y;
[FieldOffset(0x4C)]
public float ScaleX;
[FieldOffset(0x50)]
public float ScaleY;
[FieldOffset(0x54)]
public float Rotation;
[FieldOffset(0x58)]
public fixed float UnkMatrix[3 * 2];
[FieldOffset(0x70)]
public uint Color;
[FieldOffset(0x74)]
public float Depth;
[FieldOffset(0x78)]
public float Depth_2;
[FieldOffset(0x7C)]
public ushort AddRed;
[FieldOffset(0x7E)]
public ushort AddGreen;
[FieldOffset(0x80)]
public ushort AddBlue;
[FieldOffset(0x82)]
public ushort AddRed_2;
[FieldOffset(0x84)]
public ushort AddGreen_2;
[FieldOffset(0x86)]
public ushort AddBlue_2;
[FieldOffset(0x88)]
public byte MultiplyRed;
[FieldOffset(0x89)]
public byte MultiplyGreen;
[FieldOffset(0x8A)]
public byte MultiplyBlue;
[FieldOffset(0x8B)]
public byte MultiplyRed_2;
[FieldOffset(0x8C)]
public byte MultiplyGreen_2;
[FieldOffset(0x8D)]
public byte MultiplyBlue_2;
[FieldOffset(0x8E)]
public byte Alpha_2;
[FieldOffset(0x8F)]
public byte UnkByte_1;
[FieldOffset(0x90)]
public ushort Width;
[FieldOffset(0x92)]
public ushort Height;
[FieldOffset(0x94)]
public float OriginX;
[FieldOffset(0x98)]
public float OriginY;
[FieldOffset(0x9C)]
public ushort Priority;
[FieldOffset(0x9E)]
public short Flags;
[FieldOffset(0xA0)]
public uint Flags_2;
}
}

View file

@ -31,7 +31,7 @@ namespace Dalamud.Game.Internal.Gui
/// </summary>
/// <param name="scanner">The SigScanner instance.</param>
/// <param name="dalamud">The Dalamud instance.</param>
public ToastGui(SigScanner scanner, Dalamud dalamud)
internal ToastGui(SigScanner scanner, Dalamud dalamud)
{
this.dalamud = dalamud;

View file

@ -34,8 +34,8 @@ namespace Dalamud.Game.Internal.Network
this.address.Setup(scanner);
Log.Verbose("===== G A M E N E T W O R K =====");
Log.Verbose("ProcessZonePacketDown address {ProcessZonePacketDown}", this.address.ProcessZonePacketDown);
Log.Verbose("ProcessZonePacketUp address {ProcessZonePacketUp}", this.address.ProcessZonePacketUp);
Log.Verbose($"ProcessZonePacketDown address 0x{this.address.ProcessZonePacketDown.ToInt64():X}");
Log.Verbose($"ProcessZonePacketUp address 0x{this.address.ProcessZonePacketUp.ToInt64():X}");
this.processZonePacketDownHook = new Hook<ProcessZonePacketDownDelegate>(this.address.ProcessZonePacketDown, this.ProcessZonePacketDownDetour);
this.processZonePacketUpHook = new Hook<ProcessZonePacketUpDelegate>(this.address.ProcessZonePacketUp, this.ProcessZonePacketUpDetour);

View file

@ -25,7 +25,7 @@ namespace Dalamud.Game.Internal.File
/// </summary>
/// <param name="dalamud">The Dalamud instance.</param>
/// <param name="scanner">The SigScanner instance.</param>
public ResourceManager(Dalamud dalamud, SigScanner scanner)
internal ResourceManager(Dalamud dalamud, SigScanner scanner)
{
this.dalamud = dalamud;
this.address = new ResourceManagerAddressResolver();

View file

@ -31,7 +31,7 @@ namespace Dalamud.Game.Network
/// </summary>
/// <param name="dalamud">The Dalamud instance.</param>
/// <param name="optOutMbUploads">Whether the client should opt out of marketboard uploads.</param>
public NetworkHandlers(Dalamud dalamud, bool optOutMbUploads)
internal NetworkHandlers(Dalamud dalamud, bool optOutMbUploads)
{
this.dalamud = dalamud;
this.optOutMbUploads = optOutMbUploads;
@ -84,11 +84,11 @@ namespace Dalamud.Game.Network
{
var flashInfo = new NativeFunctions.FlashWindowInfo
{
cbSize = (uint)Marshal.SizeOf<NativeFunctions.FlashWindowInfo>(),
uCount = uint.MaxValue,
dwTimeout = 0,
dwFlags = NativeFunctions.FlashWindow.All | NativeFunctions.FlashWindow.TimerNoFG,
hwnd = Process.GetCurrentProcess().MainWindowHandle,
Size = (uint)Marshal.SizeOf<NativeFunctions.FlashWindowInfo>(),
Count = uint.MaxValue,
Timeout = 0,
Flags = NativeFunctions.FlashWindow.All | NativeFunctions.FlashWindow.TimerNoFG,
Hwnd = Process.GetCurrentProcess().MainWindowHandle,
};
NativeFunctions.FlashWindowEx(ref flashInfo);
}

View file

@ -34,8 +34,8 @@ namespace Dalamud.Game
if (this.IsCopy)
this.SetupCopiedSegments();
Log.Verbose("Module base: {Address}", this.TextSectionBase);
Log.Verbose("Module size: {Size}", this.TextSectionSize);
Log.Verbose($"Module base: 0x{this.TextSectionBase.ToInt64():X}");
Log.Verbose($"Module size: 0x{this.TextSectionSize:X}");
}
/// <summary>
@ -206,7 +206,7 @@ namespace Dalamud.Game
var insnByte = Marshal.ReadByte(scanRet);
if (insnByte == 0xE8 || insnByte == 0xE9)
return ReadCallSig(scanRet);
return ReadJmpCallSig(scanRet);
return scanRet;
}
@ -220,13 +220,13 @@ namespace Dalamud.Game
}
/// <summary>
/// Helper for ScanText to get the correct address for IDA sigs that mark the first CALL location.
/// Helper for ScanText to get the correct address for IDA sigs that mark the first JMP or CALL location.
/// </summary>
/// <param name="sigLocation">The address the CALL sig resolved to.</param>
/// <param name="sigLocation">The address the JMP or CALL sig resolved to.</param>
/// <returns>The real offset of the signature.</returns>
private static IntPtr ReadCallSig(IntPtr sigLocation)
private static IntPtr ReadJmpCallSig(IntPtr sigLocation)
{
var jumpOffset = Marshal.ReadInt32(IntPtr.Add(sigLocation, 1));
var jumpOffset = Marshal.ReadInt32(sigLocation, 1);
return IntPtr.Add(sigLocation, 5 + jumpOffset);
}
@ -235,6 +235,7 @@ namespace Dalamud.Game
signature = signature.Replace(" ", string.Empty);
if (signature.Length % 2 != 0)
throw new ArgumentException("Signature without whitespaces must be divisible by two.", nameof(signature));
var needleLength = signature.Length / 2;
var needle = new byte[needleLength];
var mask = new bool[needleLength];

View file

@ -88,7 +88,7 @@ namespace Dalamud.Game.Text.SeStringHandling
/// <returns>An SeString containing all the payloads necessary to display an item link in the chat log.</returns>
public SeString CreateItemLink(Item item, bool isHQ, string displayNameOverride = null)
{
return this.CreateItemLink((uint)item.RowId, isHQ, displayNameOverride ?? item.Name);
return this.CreateItemLink(item.RowId, isHQ, displayNameOverride ?? item.Name);
}
/// <summary>

View file

@ -18,26 +18,15 @@ using System.Diagnostics.CodeAnalysis;
// Extensions
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Group extensions with the relevant class", Scope = "type", Target = "~T:Dalamud.Interface.FontAwesomeExtensions")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "Enum followed by an extension", Scope = "type", Target = "~T:Dalamud.Interface.FontAwesomeExtensions")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Group extensions with the relevant class", Scope = "type", Target = "~T:Dalamud.Game.Internal.Gui.Structs.JobFlagsExtensions")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "Enum followed by an extension", Scope = "type", Target = "~T:Dalamud.Game.Internal.Gui.Structs.JobFlagsExtensions")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Group extensions with the relevant class", Scope = "type", Target = "~T:Dalamud.Game.Internal.Gui.Structs.JobFlagsExt")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "Enum followed by an extension", Scope = "type", Target = "~T:Dalamud.Game.Internal.Gui.Structs.JobFlagsExt")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Group extensions with the relevant class", Scope = "type", Target = "~T:Dalamud.Game.Text.XivChatTypeExtensions")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "Enum followed by an extension", Scope = "type", Target = "~T:Dalamud.Game.Text.XivChatTypeExtensions")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Group attributes with the relevant class", Scope = "type", Target = "~T:Dalamud.Game.Text.XivChatTypeInfoAttribute")]
// NativeFunctions.cs
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Default Microsoft naming", Scope = "type", Target = "~T:Dalamud.NativeFunctions.FlashWindowInfo")]
// EntryPoint.cs
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required by EasyHook", Scope = "member", Target = "~M:Dalamud.EntryPoint.#ctor(EasyHook.RemoteHooking.IContext,Dalamud.DalamudStartInfo)")]
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required by EasyHook", Scope = "member", Target = "~M:Dalamud.EntryPoint.Run(EasyHook.RemoteHooking.IContext,Dalamud.DalamudStartInfo)")]
// DalamudStartInfo.cs
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Interop", Scope = "type", Target = "~T:Dalamud.DalamudStartInfo")]
// AtkResNode.cs
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Inherit documentation or lack thereof from FFXIVClientStructs", Scope = "type", Target = "~T:Dalamud.Game.Internal.Gui.Structs.AtkResNode")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Inherit naming from FFXIVClientStructs", Scope = "type", Target = "~T:Dalamud.Game.Internal.Gui.Structs.AtkResNode")]
// PartyFinder
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Explicit struct layout", Scope = "type", Target = "~T:Dalamud.Game.Internal.Gui.Structs.PartyFinder.Packet")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "Explicit struct layout", Scope = "type", Target = "~T:Dalamud.Game.Internal.Gui.Structs.PartyFinder.Listing")]
@ -86,9 +75,7 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.ClientState.GamepadState")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.ClientState.Condition")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.ClientState.Targets")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Interface.InterfaceManager.LastImGuiIoPtr")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "type", Target = "~T:Dalamud.Game.ClientState.Actors.Types.PartyMember")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Interface.InterfaceManager.OnBuildFonts")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.Actors.Types.Actor.Address")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.Structs.JobGauge.BLMGauge.NumUmbralHearts")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.ClientState.Structs.JobGauge.DNCGauge.NumCompleteSteps")]
@ -97,10 +84,12 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Text.SeStringHandling.Payload.END_BYTE")]
[assembly: SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Unused, but eventually, maybe.", Scope = "member", Target = "~F:Dalamud.Game.ClientState.PartyList.address")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "breaking api change", Scope = "member", Target = "~E:Dalamud.Game.ClientState.ClientState.CfPop")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Internal.Framework.StatsHistory")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Internal.Framework.StatsHistory")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static elements should appear before instance elements", Justification = "breaking api change, move to util", Scope = "type", Target = "~T:Dalamud.Game.Text.EnumExtensions")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "breaking api change, move to util", Scope = "type", Target = "~T:Dalamud.Game.Text.EnumExtensions")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Internal.Framework.StatsHistory")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Internal.Framework.StatsHistory")]
[assembly: SuppressMessage("Usage", "CA2211:Non-constant fields should not be visible", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Internal.Framework.StatsHistory")]
[assembly: SuppressMessage("Usage", "CA2208:Instantiate argument exceptions correctly", Justification = "Appears to be a bug, it is being used correctly", Scope = "member", Target = "~M:Dalamud.Data.DataManager.Initialize(System.String)")]
// I mostly didnt care to do these.
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "breaking api change", Scope = "member", Target = "~F:Dalamud.Game.Internal.Network.GameNetwork.OnNetworkMessage")]

View file

@ -3,7 +3,8 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using EasyHook;
using CoreHook;
using Dalamud.Hooking.Internal;
namespace Dalamud.Hooking
{
@ -30,8 +31,8 @@ namespace Dalamud.Hooking
{
this.hookInfo = LocalHook.Create(address, detour, null); // Installs a hook here
this.address = address;
this.original = Marshal.GetDelegateForFunctionPointer<T>(this.hookInfo.HookBypassAddress);
HookInfo.TrackedHooks.Add(new HookInfo() { Delegate = detour, Hook = this, Assembly = Assembly.GetCallingAssembly() });
this.original = Marshal.GetDelegateForFunctionPointer<T>(this.hookInfo.OriginalAddress);
HookManager.TrackedHooks.Add(new HookInfo() { Delegate = detour, Hook = this, Assembly = Assembly.GetCallingAssembly() });
}
/// <summary>
@ -130,9 +131,8 @@ namespace Dalamud.Hooking
return;
}
this.hookInfo.Dispose();
this.IsDisposed = true;
this.hookInfo.Dispose();
}
/// <summary>

View file

@ -3,18 +3,13 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
namespace Dalamud.Hooking
namespace Dalamud.Hooking.Internal
{
/// <summary>
/// Class containing information about registered hooks.
/// </summary>
internal class HookInfo
{
/// <summary>
/// Static list of tracked and registered hooks.
/// </summary>
internal static readonly List<HookInfo> TrackedHooks = new();
private ulong? inProcessMemory = 0;
/// <summary>

View file

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
namespace Dalamud.Hooking.Internal
{
/// <summary>
/// This class manages the final disposition of hooks, cleaning up any that have not reverted their changes.
/// </summary>
internal class HookManager : IDisposable
{
// private readonly Dalamud dalamud;
/// <summary>
/// Initializes a new instance of the <see cref="HookManager"/> class.
/// </summary>
/// <param name="dalamud">Dalamud instance.</param>
public HookManager(Dalamud dalamud)
{
_ = dalamud;
// this.dalamud = dalamud;
}
/// <summary>
/// Gets a static list of tracked and registered hooks.
/// </summary>
internal static List<HookInfo> TrackedHooks { get; } = new();
/// <inheritdoc/>
public void Dispose()
{
}
}
}

View file

@ -1,6 +1,6 @@
using System;
namespace Dalamud.Hooking
namespace Dalamud.Hooking.Internal
{
/// <summary>
/// Interface describing a generic hook.

View file

@ -1,61 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Interface.Windowing;
using ImGuiNET;
namespace Dalamud.Interface.Colors
{
/// <summary>
/// color Demo Window to view custom ImGui colors.
/// </summary>
internal class ColorDemoWindow : Window
{
private readonly List<KeyValuePair<string, Vector4>> colors;
/// <summary>
/// Initializes a new instance of the <see cref="ColorDemoWindow"/> class.
/// </summary>
public ColorDemoWindow()
: base("Dalamud Colors Demo")
{
this.Size = new Vector2(600, 500);
this.SizeCondition = ImGuiCond.FirstUseEver;
this.colors = new List<KeyValuePair<string, Vector4>>
{
Demo("White", ImGuiColors.White),
Demo("DalamudRed", ImGuiColors.DalamudRed),
Demo("DalamudGrey", ImGuiColors.DalamudGrey),
Demo("DalamudGrey2", ImGuiColors.DalamudGrey2),
Demo("DalamudGrey3", ImGuiColors.DalamudGrey3),
Demo("DalamudWhite", ImGuiColors.DalamudWhite),
Demo("DalamudWhite2", ImGuiColors.DalamudWhite2),
Demo("DalamudOrange", ImGuiColors.DalamudOrange),
Demo("TankBlue", ImGuiColors.TankBlue),
Demo("HealerGreen", ImGuiColors.HealerGreen),
Demo("DPSRed", ImGuiColors.DPSRed),
};
this.colors = this.colors.OrderBy(colorDemo => colorDemo.Key).ToList();
}
/// <inheritdoc/>
public override void Draw()
{
ImGui.BeginChild("color_scrolling", new Vector2(0, 0), false, ImGuiWindowFlags.AlwaysVerticalScrollbar | ImGuiWindowFlags.HorizontalScrollbar);
ImGui.Text("This is a collection of UI colors you can use in your plugin.");
ImGui.Separator();
foreach (var color in this.colors)
{
ImGui.TextColored(color.Value, color.Key);
}
ImGui.EndChild();
}
private static KeyValuePair<string, Vector4> Demo(string name, Vector4 color)
{
return new KeyValuePair<string, Vector4>(name, color);
}
}
}

View file

@ -0,0 +1,76 @@
using System.Numerics;
using ImGuiNET;
namespace Dalamud.Interface.Components
{
/// <summary>
/// Class containing various methods providing ImGui components.
/// </summary>
public static partial class ImGuiComponents
{
/// <summary>
/// Alpha modified IconButton component to use an icon as a button with alpha and color options.
/// </summary>
/// <param name="icon">The icon for the button.</param>
/// <param name="id">The ID of the button.</param>
/// <param name="defaultColor">The default color of the button.</param>
/// <param name="activeColor">The color of the button when active.</param>
/// <param name="hoveredColor">The color of the button when hovered.</param>
/// <param name="alphaMult">A multiplier for the current alpha levels.</param>
/// <returns>Indicator if button is clicked.</returns>
public static bool DisabledButton(FontAwesomeIcon icon, int? id = null, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null, float alphaMult = .5f)
{
ImGui.PushFont(UiBuilder.IconFont);
var text = icon.ToIconString();
if (id.HasValue)
text = $"{text}{id}";
var button = DisabledButton(text, defaultColor, activeColor, hoveredColor, alphaMult);
ImGui.PopFont();
return button;
}
/// <summary>
/// Alpha modified Button component to use as a disabled button with alpha and color options.
/// </summary>
/// <param name="labelWithId">The button label with ID.</param>
/// <param name="defaultColor">The default color of the button.</param>
/// <param name="activeColor">The color of the button when active.</param>
/// <param name="hoveredColor">The color of the button when hovered.</param>
/// <param name="alphaMult">A multiplier for the current alpha levels.</param>
/// <returns>Indicator if button is clicked.</returns>
public static bool DisabledButton(string labelWithId, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null, float alphaMult = .5f)
{
if (defaultColor.HasValue)
ImGui.PushStyleColor(ImGuiCol.Button, defaultColor.Value);
if (activeColor.HasValue)
ImGui.PushStyleColor(ImGuiCol.ButtonActive, activeColor.Value);
if (hoveredColor.HasValue)
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, hoveredColor.Value);
var style = ImGui.GetStyle();
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, style.Alpha * alphaMult);
var button = ImGui.Button(labelWithId);
ImGui.PopStyleVar();
if (defaultColor.HasValue)
ImGui.PopStyleColor();
if (activeColor.HasValue)
ImGui.PopStyleColor();
if (hoveredColor.HasValue)
ImGui.PopStyleColor();
return button;
}
}
}

View file

@ -9,6 +9,14 @@ namespace Dalamud.Interface.Components
/// </summary>
public static partial class ImGuiComponents
{
/// <summary>
/// IconButton component to use an icon as a button.
/// </summary>
/// <param name="icon">The icon for the button.</param>
/// <returns>Indicator if button is clicked.</returns>
public static bool IconButton(FontAwesomeIcon icon)
=> IconButton(icon, null, null, null);
/// <summary>
/// IconButton component to use an icon as a button.
/// </summary>
@ -16,16 +24,26 @@ namespace Dalamud.Interface.Components
/// <param name="icon">The icon for the button.</param>
/// <returns>Indicator if button is clicked.</returns>
public static bool IconButton(int id, FontAwesomeIcon icon)
{
ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero);
ImGui.PushStyleColor(ImGuiCol.ButtonActive, Vector4.Zero);
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, Vector4.Zero);
ImGui.PushFont(UiBuilder.IconFont);
var button = ImGui.Button($"{icon.ToIconString()}{id}");
ImGui.PopFont();
ImGui.PopStyleColor(3);
return button;
}
=> IconButton(id, icon, null, null, null);
/// <summary>
/// IconButton component to use an icon as a button.
/// </summary>
/// <param name="iconText">Text already containing the icon string.</param>
/// <returns>Indicator if button is clicked.</returns>
public static bool IconButton(string iconText)
=> IconButton(iconText, null, null, null);
/// <summary>
/// IconButton component to use an icon as a button.
/// </summary>
/// <param name="icon">The icon for the button.</param>
/// <param name="defaultColor">The default color of the button.</param>
/// <param name="activeColor">The color of the button when active.</param>
/// <param name="hoveredColor">The color of the button when hovered.</param>
/// <returns>Indicator if button is clicked.</returns>
public static bool IconButton(FontAwesomeIcon icon, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null)
=> IconButton($"{icon.ToIconString()}", defaultColor, activeColor, hoveredColor);
/// <summary>
/// IconButton component to use an icon as a button with color options.
@ -36,15 +54,48 @@ namespace Dalamud.Interface.Components
/// <param name="activeColor">The color of the button when active.</param>
/// <param name="hoveredColor">The color of the button when hovered.</param>
/// <returns>Indicator if button is clicked.</returns>
public static bool IconButton(int id, FontAwesomeIcon icon, Vector4 defaultColor, Vector4 activeColor, Vector4 hoveredColor)
public static bool IconButton(int id, FontAwesomeIcon icon, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null)
=> IconButton($"{icon.ToIconString()}{id}", defaultColor, activeColor, hoveredColor);
/// <summary>
/// IconButton component to use an icon as a button with color options.
/// </summary>
/// <param name="iconText">Text already containing the icon string.</param>
/// <param name="defaultColor">The default color of the button.</param>
/// <param name="activeColor">The color of the button when active.</param>
/// <param name="hoveredColor">The color of the button when hovered.</param>
/// <returns>Indicator if button is clicked.</returns>
public static bool IconButton(string iconText, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null)
{
ImGui.PushStyleColor(ImGuiCol.Button, defaultColor);
ImGui.PushStyleColor(ImGuiCol.ButtonActive, activeColor);
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, hoveredColor);
var numColors = 0;
if (defaultColor.HasValue)
{
ImGui.PushStyleColor(ImGuiCol.Button, defaultColor.Value);
numColors++;
}
if (activeColor.HasValue)
{
ImGui.PushStyleColor(ImGuiCol.ButtonActive, activeColor.Value);
numColors++;
}
if (hoveredColor.HasValue)
{
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, hoveredColor.Value);
numColors++;
}
ImGui.PushFont(UiBuilder.IconFont);
var button = ImGui.Button($"{icon.ToIconString()}{id}");
var button = ImGui.Button(iconText);
ImGui.PopFont();
ImGui.PopStyleColor(3);
if (numColors > 0)
ImGui.PopStyleColor(numColors);
return button;
}
}

View file

@ -13,8 +13,7 @@ namespace Dalamud.Interface.Components
/// <param name="label">The label for text.</param>
/// <param name="value">The text value.</param>
/// <param name="hint">The hint to show on hover.</param>
public static void TextWithLabel(
string label, string value, string hint = "")
public static void TextWithLabel(string label, string value, string hint = "")
{
ImGui.Text(label + ": ");
ImGui.SameLine();

View file

@ -20,6 +20,24 @@ namespace Dalamud.Interface
/// </summary>
public static float GlobalScale { get; private set; }
/// <summary>
/// Gets a <see cref="Vector2"/> that is pre-scaled with the <see cref="GlobalScale"/> multiplier.
/// </summary>
/// <param name="x">Vector2 X parameter.</param>
/// <param name="y">Vector2 Y parameter.</param>
/// <returns>A scaled Vector2.</returns>
public static Vector2 ScaledVector2(float x, float y) => new Vector2(x, y) * GlobalScale;
/// <summary>
/// Gets a <see cref="Vector4"/> that is pre-scaled with the <see cref="GlobalScale"/> multiplier.
/// </summary>
/// <param name="x">Vector4 X parameter.</param>
/// <param name="y">Vector4 Y parameter.</param>
/// <param name="z">Vector4 Z parameter.</param>
/// <param name="w">Vector4 W parameter.</param>
/// <returns>A scaled Vector2.</returns>
public static Vector4 ScaledVector4(float x, float y, float z, float w) => new Vector4(x, y, z, w) * GlobalScale;
/// <summary>
/// Force the next ImGui window to stay inside the main game window.
/// </summary>
@ -29,7 +47,14 @@ namespace Dalamud.Interface
/// Create a dummy scaled by the global Dalamud scale.
/// </summary>
/// <param name="size">The size of the dummy.</param>
public static void ScaledDummy(float size) => ImGui.Dummy(new Vector2(size * GlobalScale, size * GlobalScale));
public static void ScaledDummy(float size) => ScaledDummy(size, size);
/// <summary>
/// Create a dummy scaled by the global Dalamud scale.
/// </summary>
/// <param name="x">Vector2 X parameter.</param>
/// <param name="y">Vector2 Y parameter.</param>
public static void ScaledDummy(float x, float y) => ScaledDummy(new Vector2(x, y));
/// <summary>
/// Create a dummy scaled by the global Dalamud scale.

View file

@ -7,7 +7,7 @@ using CheapLoc;
using Dalamud.Game.Command;
using Serilog;
namespace Dalamud
namespace Dalamud.Interface.Internal
{
/// <summary>
/// Class handling Dalamud core commands.
@ -149,7 +149,7 @@ namespace Dalamud
try
{
this.dalamud.PluginManager.ReloadPlugins();
this.dalamud.PluginManager.ReloadAllPlugins();
this.dalamud.Framework.Gui.Chat.Print("OK");
}
@ -226,20 +226,20 @@ namespace Dalamud
private void OnDebugDrawDevMenu(string command, string arguments)
{
this.dalamud.DalamudUi.IsDevMenu = !this.dalamud.DalamudUi.IsDevMenu;
this.dalamud.DalamudUi.ToggleDevMenu();
}
private void OnDebugDrawDataMenu(string command, string arguments)
{
if (string.IsNullOrEmpty(arguments))
this.dalamud.DalamudUi.ToggleData();
this.dalamud.DalamudUi.ToggleDataWindow();
else
this.dalamud.DalamudUi.ToggleData(arguments);
this.dalamud.DalamudUi.ToggleDataWindow(arguments);
}
private void OnOpenLog(string command, string arguments)
{
this.dalamud.DalamudUi.ToggleLog();
this.dalamud.DalamudUi.ToggleLogWindow();
}
private void OnDebugImInfoCommand(string command, string arguments)
@ -267,12 +267,12 @@ namespace Dalamud
private void OnOpenInstallerCommand(string command, string arguments)
{
this.dalamud.DalamudUi.TogglePluginInstaller();
this.dalamud.DalamudUi.TogglePluginInstallerWindow();
}
private void OnOpenCreditsCommand(string command, string arguments)
{
this.dalamud.DalamudUi.ToggleCredits();
this.dalamud.DalamudUi.ToggleCreditsWindow();
}
private void OnSetLanguageCommand(string command, string arguments)
@ -299,7 +299,7 @@ namespace Dalamud
private void OnOpenSettingsCommand(string command, string arguments)
{
this.dalamud.DalamudUi.ToggleSettings();
this.dalamud.DalamudUi.ToggleSettingsWindow();
}
}
}

View file

@ -1,152 +1,288 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using CheapLoc;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Scratchpad;
using Dalamud.Interface.Internal.Windows;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using Dalamud.Plugin.Internal;
using ImGuiNET;
using Serilog;
using Serilog.Events;
namespace Dalamud.Interface
namespace Dalamud.Interface.Internal
{
/// <summary>
/// Class handling Dalamud core interface.
/// This plugin implements all of the Dalamud interface separately, to allow for reloading of the interface and rapid prototyping.
/// </summary>
internal class DalamudInterface : IDisposable
{
private static readonly ModuleLog Log = new("DUI");
private readonly Dalamud dalamud;
private readonly WindowSystem windowSystem;
private readonly DalamudLogWindow logWindow;
private readonly DalamudDataWindow dataWindow;
private readonly DalamudCreditsWindow creditsWindow;
private readonly DalamudSettingsWindow settingsWindow;
private readonly PluginInstallerWindow pluginWindow;
private readonly DalamudPluginStatWindow pluginStatWindow;
private readonly DalamudChangelogWindow changelogWindow;
private readonly ComponentDemoWindow componentDemoWindow;
private readonly ChangelogWindow changelogWindow;
private readonly ColorDemoWindow colorDemoWindow;
private readonly ScratchpadWindow scratchpadWindow;
private readonly ComponentDemoWindow componentDemoWindow;
private readonly CreditsWindow creditsWindow;
private readonly DataWindow dataWindow;
private readonly GamepadModeNotifierWindow gamepadModeNotifierWindow;
private readonly WindowSystem windowSystem = new("DalamudCore");
private readonly LogWindow logWindow;
private readonly PluginStatWindow pluginStatWindow;
private readonly PluginInstallerWindow pluginWindow;
private readonly ScratchpadWindow scratchpadWindow;
private readonly SettingsWindow settingsWindow;
private ulong frameCount = 0;
private bool isImguiDrawDemoWindow = false;
#if DEBUG
private bool isImguiDrawDevMenu = true;
private bool isImGuiDrawDevMenu = true;
#else
private bool isImguiDrawDevMenu = false;
private bool isImGuiDrawDevMenu = false;
#endif
private bool isImGuiDrawDemoWindow = false;
/// <summary>
/// Initializes a new instance of the <see cref="DalamudInterface"/> class.
/// </summary>
/// <param name="dalamud">The Dalamud instance to register to.</param>
/// <param name="dalamud">The Dalamud instance.</param>
public DalamudInterface(Dalamud dalamud)
{
this.dalamud = dalamud;
this.windowSystem = new WindowSystem("DalamudCore");
this.logWindow = new DalamudLogWindow(this.dalamud.CommandManager, this.dalamud.Configuration)
{
IsOpen = false,
};
this.windowSystem.AddWindow(this.logWindow);
this.changelogWindow = new ChangelogWindow(dalamud) { IsOpen = false };
this.colorDemoWindow = new ColorDemoWindow() { IsOpen = false };
this.componentDemoWindow = new ComponentDemoWindow() { IsOpen = false };
this.creditsWindow = new CreditsWindow(dalamud) { IsOpen = false };
this.dataWindow = new DataWindow(dalamud) { IsOpen = false };
this.gamepadModeNotifierWindow = new GamepadModeNotifierWindow();
this.logWindow = new LogWindow(dalamud) { IsOpen = this.dalamud.Configuration.LogOpenAtStartup };
this.pluginStatWindow = new PluginStatWindow(dalamud) { IsOpen = false };
this.pluginWindow = new PluginInstallerWindow(dalamud) { IsOpen = false };
this.scratchpadWindow = new ScratchpadWindow(dalamud) { IsOpen = false };
this.settingsWindow = new SettingsWindow(dalamud) { IsOpen = false };
this.dataWindow = new DalamudDataWindow(this.dalamud)
{
IsOpen = false,
};
this.windowSystem.AddWindow(this.dataWindow);
this.creditsWindow = new DalamudCreditsWindow(this.dalamud)
{
IsOpen = false,
};
this.windowSystem.AddWindow(this.changelogWindow);
this.windowSystem.AddWindow(this.colorDemoWindow);
this.windowSystem.AddWindow(this.componentDemoWindow);
this.windowSystem.AddWindow(this.creditsWindow);
this.settingsWindow = new DalamudSettingsWindow(this.dalamud)
{
IsOpen = false,
};
this.windowSystem.AddWindow(this.dataWindow);
this.windowSystem.AddWindow(this.gamepadModeNotifierWindow);
this.windowSystem.AddWindow(this.logWindow);
this.windowSystem.AddWindow(this.pluginStatWindow);
this.windowSystem.AddWindow(this.pluginWindow);
this.windowSystem.AddWindow(this.scratchpadWindow);
this.windowSystem.AddWindow(this.settingsWindow);
this.pluginWindow = new PluginInstallerWindow(this.dalamud, this.dalamud.StartInfo.GameVersion)
{
IsOpen = false,
};
this.windowSystem.AddWindow(this.pluginWindow);
this.dalamud.InterfaceManager.OnDraw += this.OnDraw;
this.pluginStatWindow = new DalamudPluginStatWindow(this.dalamud.PluginManager)
{
IsOpen = false,
};
this.windowSystem.AddWindow(this.pluginStatWindow);
this.changelogWindow = new DalamudChangelogWindow(this.dalamud)
{
IsOpen = false,
};
this.windowSystem.AddWindow(this.changelogWindow);
this.componentDemoWindow = new ComponentDemoWindow()
{
IsOpen = false,
};
this.windowSystem.AddWindow(this.componentDemoWindow);
this.colorDemoWindow = new ColorDemoWindow()
{
IsOpen = false,
};
this.windowSystem.AddWindow(this.colorDemoWindow);
this.scratchpadWindow = new ScratchpadWindow(this.dalamud)
{
IsOpen = false,
};
this.windowSystem.AddWindow(this.scratchpadWindow);
this.gamepadModeNotifierWindow = new GamepadModeNotifierWindow();
this.windowSystem.AddWindow(this.gamepadModeNotifierWindow);
Log.Information("[DUI] Windows added");
if (dalamud.Configuration.LogOpenAtStartup)
this.OpenLog();
Log.Information("Windows added");
}
/// <summary>
/// Gets or sets a value indicating whether the Dalamud dev menu is drawing.
/// Gets or sets a value indicating whether the /xldev menu is open.
/// </summary>
public bool IsDevMenu
public bool IsDevMenuOpen
{
get => this.isImguiDrawDevMenu;
set => this.isImguiDrawDevMenu = value;
get => this.isImGuiDrawDevMenu;
set => this.isImGuiDrawDevMenu = value;
}
/// <summary>
/// Draw the Dalamud core interface via ImGui.
/// Gets a value indicating whether the current Dalamud version warrants displaying the changelog.
/// </summary>
public void Draw()
public bool WarrantsChangelog => ChangelogWindow.WarrantsChangelog;
/// <inheritdoc/>
public void Dispose()
{
this.dalamud.InterfaceManager.OnDraw -= this.OnDraw;
this.windowSystem.RemoveAllWindows();
this.creditsWindow.Dispose();
this.logWindow.Dispose();
this.scratchpadWindow.Dispose();
}
#region Open
/// <summary>
/// Opens the <see cref="ChangelogWindow"/>.
/// </summary>
public void OpenChangelogWindow() => this.changelogWindow.IsOpen = true;
/// <summary>
/// Opens the <see cref="ColorDemoWindow"/>.
/// </summary>
public void OpenColorsDemoWindow() => this.colorDemoWindow.IsOpen = true;
/// <summary>
/// Opens the <see cref="ComponentDemoWindow"/>.
/// </summary>
public void OpenComponentDemoWindow() => this.componentDemoWindow.IsOpen = true;
/// <summary>
/// Opens the <see cref="CreditsWindow"/>.
/// </summary>
public void OpenCreditsWindow() => this.creditsWindow.IsOpen = true;
/// <summary>
/// Opens the <see cref="DataWindow"/>.
/// </summary>
/// <param name="dataKind">The data kind to switch to after opening.</param>
public void OpenDataWindow(string dataKind = null)
{
this.dataWindow.IsOpen = true;
if (dataKind != null && this.dataWindow.IsOpen)
{
this.dataWindow.SetDataKind(dataKind);
}
}
/// <summary>
/// Opens the dev menu bar.
/// </summary>
public void OpenDevMenu() => this.isImGuiDrawDevMenu = true;
/// <summary>
/// Opens the <see cref="GamepadModeNotifierWindow"/>.
/// </summary>
public void OpenGamepadModeNotifierWindow() => this.gamepadModeNotifierWindow.IsOpen = true;
/// <summary>
/// Opens the <see cref="LogWindow"/>.
/// </summary>
public void OpenLogWindow() => this.logWindow.IsOpen = true;
/// <summary>
/// Opens the <see cref="PluginStatWindow"/>.
/// </summary>
public void OpenPluginStats() => this.pluginStatWindow.IsOpen = true;
/// <summary>
/// Opens the <see cref="PluginInstallerWindow"/>.
/// </summary>
public void OpenPluginInstaller() => this.pluginWindow.IsOpen = true;
/// <summary>
/// Opens the <see cref="ScratchpadWindow"/>.
/// </summary>
public void OpenScratchpadWindow() => this.scratchpadWindow.IsOpen = true;
/// <summary>
/// Opens the <see cref="SettingsWindow"/>.
/// </summary>
public void OpenSettings() => this.settingsWindow.IsOpen = true;
#endregion
#region Toggle
/// <summary>
/// Toggles the <see cref="ChangelogWindow"/>.
/// </summary>
public void ToggleChangelogWindow() => this.changelogWindow.Toggle();
/// <summary>
/// Toggles the <see cref="ColorDemoWindow"/>.
/// </summary>
public void ToggleColorsDemoWindow() => this.colorDemoWindow.Toggle();
/// <summary>
/// Toggles the <see cref="ComponentDemoWindow"/>.
/// </summary>
public void ToggleComponentDemoWindow() => this.componentDemoWindow.Toggle();
/// <summary>
/// Toggles the <see cref="CreditsWindow"/>.
/// </summary>
public void ToggleCreditsWindow() => this.creditsWindow.Toggle();
/// <summary>
/// Toggles the <see cref="DataWindow"/>.
/// </summary>
/// <param name="dataKind">The data kind to switch to after opening.</param>
public void ToggleDataWindow(string dataKind = null)
{
this.dataWindow.Toggle();
if (dataKind != null && this.dataWindow.IsOpen)
{
this.dataWindow.SetDataKind(dataKind);
}
}
/// <summary>
/// Toggles the dev menu bar.
/// </summary>
public void ToggleDevMenu() => this.isImGuiDrawDevMenu ^= true;
/// <summary>
/// Toggles the <see cref="GamepadModeNotifierWindow"/>.
/// </summary>
public void ToggleGamepadModeNotifierWindow() => this.gamepadModeNotifierWindow.Toggle();
/// <summary>
/// Toggles the <see cref="LogWindow"/>.
/// </summary>
public void ToggleLogWindow() => this.logWindow.Toggle();
/// <summary>
/// Toggles the <see cref="PluginStatWindow"/>.
/// </summary>
public void TogglePluginStatsWindow() => this.pluginStatWindow.Toggle();
/// <summary>
/// Toggles the <see cref="PluginInstallerWindow"/>.
/// </summary>
public void TogglePluginInstallerWindow() => this.pluginWindow.Toggle();
/// <summary>
/// Toggles the <see cref="ScratchpadWindow"/>.
/// </summary>
public void ToggleScratchpadWindow() => this.scratchpadWindow.Toggle();
/// <summary>
/// Toggles the <see cref="SettingsWindow"/>.
/// </summary>
public void ToggleSettingsWindow() => this.settingsWindow.Toggle();
#endregion
private void OnDraw()
{
this.frameCount++;
if (!this.IsDevMenu && !this.dalamud.ClientState.Condition.Any())
try
{
ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0, 0, 0, 0));
ImGui.PushStyleColor(ImGuiCol.ButtonActive, new Vector4(0, 0, 0, 0));
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(0, 0, 0, 0));
this.DrawHiddenDevMenuOpener();
this.DrawDevMenu();
if (this.dalamud.Framework.Gui.GameUiHidden)
return;
this.windowSystem.Draw();
if (this.isImGuiDrawDemoWindow)
ImGui.ShowDemoWindow();
}
catch (Exception ex)
{
PluginLog.Error(ex, "Error during OnDraw");
}
}
private void DrawHiddenDevMenuOpener()
{
if (!this.isImGuiDrawDevMenu && !this.dalamud.ClientState.Condition.Any())
{
ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero);
ImGui.PushStyleColor(ImGuiCol.ButtonActive, Vector4.Zero);
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, Vector4.Zero);
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(0, 0, 0, 1));
ImGui.PushStyleColor(ImGuiCol.TextSelectedBg, new Vector4(0, 0, 0, 1));
ImGui.PushStyleColor(ImGuiCol.Border, new Vector4(0, 0, 0, 1));
@ -160,27 +296,30 @@ namespace Dalamud.Interface
if (ImGui.Begin("DevMenu Opener", ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoSavedSettings))
{
if (ImGui.Button("###devMenuOpener", new Vector2(40, 25)))
this.IsDevMenu = true;
this.isImGuiDrawDevMenu = true;
ImGui.End();
}
ImGui.PopStyleColor(8);
}
}
if (this.IsDevMenu)
private void DrawDevMenu()
{
if (this.isImGuiDrawDevMenu)
{
if (ImGui.BeginMainMenuBar())
{
if (ImGui.BeginMenu("Dalamud"))
{
ImGui.MenuItem("Draw Dalamud dev menu", string.Empty, ref this.isImguiDrawDevMenu);
ImGui.MenuItem("Draw Dalamud dev menu", string.Empty, ref this.isImGuiDrawDevMenu);
ImGui.Separator();
if (ImGui.MenuItem("Open Log window"))
{
this.OpenLog();
this.OpenLogWindow();
}
if (ImGui.BeginMenu("Set log level..."))
@ -214,12 +353,12 @@ namespace Dalamud.Interface
if (ImGui.MenuItem("Open Data window"))
{
this.OpenData();
this.OpenDataWindow();
}
if (ImGui.MenuItem("Open Credits window"))
{
this.OpenCredits();
this.OpenCreditsWindow();
}
if (ImGui.MenuItem("Open Settings window"))
@ -229,20 +368,20 @@ namespace Dalamud.Interface
if (ImGui.MenuItem("Open Changelog window"))
{
this.OpenChangelog();
this.OpenChangelogWindow();
}
if (ImGui.MenuItem("Open Components Demo"))
{
this.OpenComponentDemo();
this.OpenComponentDemoWindow();
}
if (ImGui.MenuItem("Open Colors Demo"))
{
this.OpenColorsDemo();
this.OpenColorsDemoWindow();
}
ImGui.MenuItem("Draw ImGui demo", string.Empty, ref this.isImguiDrawDemoWindow);
ImGui.MenuItem("Draw ImGui demo", string.Empty, ref this.isImGuiDrawDemoWindow);
ImGui.Separator();
@ -264,12 +403,12 @@ namespace Dalamud.Interface
ImGui.Separator();
if (ImGui.MenuItem("Enable Dalamud testing", string.Empty, this.dalamud.Configuration.DoDalamudTest))
{
this.dalamud.Configuration.DoDalamudTest = !this.dalamud.Configuration.DoDalamudTest;
this.dalamud.Configuration.DoDalamudTest ^= true;
this.dalamud.Configuration.Save();
}
ImGui.MenuItem(Util.AssemblyVersion, false);
ImGui.MenuItem(this.dalamud.StartInfo.GameVersion, false);
ImGui.MenuItem(this.dalamud.StartInfo.GameVersion.ToString(), false);
ImGui.EndMenu();
}
@ -300,10 +439,10 @@ namespace Dalamud.Interface
if (ImGui.MenuItem("Print plugin info"))
{
foreach (var plugin in this.dalamud.PluginManager.Plugins)
foreach (var plugin in this.dalamud.PluginManager.InstalledPlugins)
{
// TODO: some more here, state maybe?
Log.Information($"{plugin.Plugin.Name}");
PluginLog.Information($"{plugin.Name}");
}
}
@ -311,18 +450,23 @@ namespace Dalamud.Interface
{
try
{
this.dalamud.PluginManager.ReloadPlugins();
this.dalamud.PluginManager.ReloadAllPlugins();
}
catch (Exception ex)
{
this.dalamud.Framework.Gui.Chat.PrintError("Reload failed.");
Log.Error(ex, "Plugin reload failed.");
PluginLog.Error(ex, "Plugin reload failed.");
}
}
if (ImGui.MenuItem("Scan dev plugins"))
{
this.dalamud.PluginManager.ScanDevPlugins();
}
ImGui.Separator();
ImGui.MenuItem("API Level:" + PluginManager.DalamudApiLevel, false);
ImGui.MenuItem("Loaded plugins:" + this.dalamud.PluginManager?.Plugins.Count, false);
ImGui.MenuItem("Loaded plugins:" + this.dalamud.PluginManager?.InstalledPlugins.Count, false);
ImGui.EndMenu();
}
@ -384,197 +528,6 @@ namespace Dalamud.Interface
ImGui.EndMainMenuBar();
}
}
if (this.dalamud.Framework.Gui.GameUiHidden)
return;
this.windowSystem.Draw();
if (this.isImguiDrawDemoWindow)
ImGui.ShowDemoWindow();
}
/// <summary>
/// Dispose the window system and all windows that require it.
/// </summary>
public void Dispose()
{
this.scratchpadWindow.Dispose();
this.windowSystem.RemoveAllWindows();
this.logWindow?.Dispose();
this.creditsWindow?.Dispose();
}
/// <summary>
/// Open the Plugin Installer window.
/// </summary>
internal void OpenPluginInstaller()
{
this.pluginWindow.IsOpen = true;
}
/// <summary>
/// Open the changelog window.
/// </summary>
internal void OpenChangelog()
{
this.changelogWindow.IsOpen = true;
}
/// <summary>
/// Open the settings window.
/// </summary>
internal void OpenSettings()
{
this.settingsWindow.IsOpen = true;
}
/// <summary>
/// Open the log window.
/// </summary>
internal void OpenLog()
{
this.logWindow.IsOpen = true;
}
/// <summary>
/// Open the data window.
/// </summary>
internal void OpenData()
{
this.dataWindow.IsOpen = true;
}
/// <summary>
/// Open the credits window.
/// </summary>
internal void OpenCredits()
{
this.creditsWindow.IsOpen = true;
}
/// <summary>
/// Open the stats window.
/// </summary>
internal void OpenPluginStats()
{
this.pluginStatWindow.IsOpen = true;
}
/// <summary>
/// Open the component test window.
/// </summary>
internal void OpenComponentDemo()
{
this.componentDemoWindow.IsOpen = true;
}
/// <summary>
/// Open the colors test window.
/// </summary>
internal void OpenColorsDemo()
{
this.colorDemoWindow.IsOpen = true;
}
/// <summary>
/// Open the colors test window.
/// </summary>
internal void OpenScratchpadWindow()
{
this.scratchpadWindow.IsOpen = true;
}
/// <summary>
/// Toggle the Plugin Installer window.
/// </summary>
internal void TogglePluginInstaller()
{
this.pluginWindow.IsOpen ^= true;
}
/// <summary>
/// Toggle the changelog window.
/// </summary>
internal void ToggleChangelog()
{
this.changelogWindow.IsOpen ^= true;
}
/// <summary>
/// Toggle the settings window.
/// </summary>
internal void ToggleSettings()
{
this.settingsWindow.IsOpen ^= true;
}
/// <summary>
/// Toggle the log window.
/// </summary>
internal void ToggleLog()
{
this.logWindow.IsOpen ^= true;
}
/// <summary>
/// Toggle the data window.
/// </summary>
internal void ToggleData()
{
this.dataWindow.IsOpen ^= true;
}
/// <summary>
/// Toggle the data window and preset the dropdown.
/// </summary>
/// <param name="dataKind">The data kind to toggle.</param>
internal void ToggleData(string dataKind)
{
this.dataWindow.IsOpen ^= true;
if (this.dataWindow.IsOpen)
this.dataWindow.SetDataKind(dataKind);
}
/// <summary>
/// Toggle the credits window.
/// </summary>
internal void ToggleCredits()
{
this.creditsWindow.IsOpen ^= true;
}
/// <summary>
/// Toggle the stats window.
/// </summary>
internal void TogglePluginStats()
{
this.pluginStatWindow.IsOpen ^= true;
}
/// <summary>
/// Toggle the component test window.
/// </summary>
internal void ToggleComponentDemo()
{
this.componentDemoWindow.IsOpen ^= true;
}
/// <summary>
/// Toggle the scratchpad window.
/// </summary>
internal void ToggleScratchpadWindow()
{
this.scratchpadWindow.IsOpen ^= true;
}
/// <summary>
/// Toggle the gamepad notifier window window.
/// </summary>
internal void ToggleGamePadNotifierWindow()
{
this.gamepadModeNotifierWindow.IsOpen ^= true;
}
}
}

View file

@ -10,7 +10,6 @@ using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.Internal.DXGI;
using Dalamud.Hooking;
using EasyHook;
using ImGuiNET;
using ImGuiScene;
using Serilog;
@ -27,35 +26,24 @@ using SharpDX.Direct3D11;
* - Might eventually want to render to a separate target and composite, especially with reshade etc in the mix.
*/
namespace Dalamud.Interface
namespace Dalamud.Interface.Internal
{
/// <summary>
/// This class manages interaction with the ImGui interface.
/// </summary>
internal class InterfaceManager : IDisposable
{
/// <summary>
/// Code that is exexuted when fonts are rebuilt.
/// </summary>
public Action OnBuildFonts;
/// <summary>
/// The pointer to ImGui.IO(), when it last used..
/// </summary>
public ImGuiIOPtr LastImGuiIoPtr;
private readonly Dalamud dalamud;
private readonly string rtssPath;
private readonly Hook<PresentDelegate> presentHook;
private readonly Hook<ResizeBuffersDelegate> resizeBuffersHook;
private readonly Hook<SetCursorDelegate> setCursorHook;
private ManualResetEvent fontBuildSignal;
private ISwapChainAddressResolver address;
private readonly ManualResetEvent fontBuildSignal;
private readonly ISwapChainAddressResolver address;
private RawDX11Scene scene;
private string rtssPath;
// can't access imgui IO before first present call
private bool lastWantCapture = false;
private bool isRebuildingFonts = false;
@ -95,12 +83,12 @@ namespace Dalamud.Interface
try
{
var rtss = NativeFunctions.GetModuleHandle("RTSSHooks64.dll");
var rtss = NativeFunctions.GetModuleHandleW("RTSSHooks64.dll");
if (rtss != IntPtr.Zero)
{
var fileName = new StringBuilder(255);
NativeFunctions.GetModuleFileName(rtss, fileName, fileName.Capacity);
_ = NativeFunctions.GetModuleFileNameW(rtss, fileName, fileName.Capacity);
this.rtssPath = fileName.ToString();
Log.Verbose("RTSS at {0}", this.rtssPath);
@ -113,17 +101,16 @@ namespace Dalamud.Interface
Log.Error(e, "RTSS Free failed");
}
var setCursorAddr = LocalHook.GetProcAddress("user32.dll", "SetCursor");
var user32 = NativeFunctions.GetModuleHandleW("user32.dll");
var setCursorAddr = NativeFunctions.GetProcAddress(user32, "SetCursor");
Log.Verbose("===== S W A P C H A I N =====");
Log.Verbose("SetCursor address {SetCursor}", setCursorAddr);
Log.Verbose("Present address {Present}", this.address.Present);
Log.Verbose("ResizeBuffers address {ResizeBuffers}", this.address.ResizeBuffers);
Log.Verbose($"SetCursor address 0x{setCursorAddr.ToInt64():X}");
Log.Verbose($"Present address 0x{this.address.Present.ToInt64():X}");
Log.Verbose($"ResizeBuffers address 0x{this.address.ResizeBuffers.ToInt64():X}");
this.setCursorHook = new Hook<SetCursorDelegate>(setCursorAddr, this.SetCursorDetour);
this.presentHook = new Hook<PresentDelegate>(this.address.Present, this.PresentDetour);
this.resizeBuffersHook = new Hook<ResizeBuffersDelegate>(this.address.ResizeBuffers, this.ResizeBuffersDetour);
}
@ -153,6 +140,16 @@ namespace Dalamud.Interface
/// </summary>
public static ImFontPtr IconFont { get; private set; }
/// <summary>
/// Gets or sets an action that is exexuted when fonts are rebuilt.
/// </summary>
public Action OnBuildFonts { get; set; }
/// <summary>
/// Gets or sets the pointer to ImGui.IO(), when it was last used.
/// </summary>
public ImGuiIOPtr LastImGuiIoPtr { get; set; }
/// <summary>
/// Gets the D3D11 device instance.
/// </summary>
@ -195,11 +192,11 @@ namespace Dalamud.Interface
{
if (!string.IsNullOrEmpty(this.rtssPath))
{
NativeFunctions.LoadLibrary(this.rtssPath);
NativeFunctions.LoadLibraryW(this.rtssPath);
var rtssModule = NativeFunctions.GetModuleHandleW("RTSSHooks64.dll");
var installAddr = NativeFunctions.GetProcAddress(rtssModule, "InstallRTSSHook");
var installAddr = LocalHook.GetProcAddress("RTSSHooks64.dll", "InstallRTSSHook");
var installDele = Marshal.GetDelegateForFunctionPointer<InstallRTSSHook>(installAddr);
installDele.Invoke();
Marshal.GetDelegateForFunctionPointer<InstallRTSSHook>(installAddr).Invoke();
}
}
catch (Exception ex)
@ -567,7 +564,7 @@ namespace Dalamud.Interface
{
ImGui.GetIO().ConfigFlags ^= ImGuiConfigFlags.NavEnableGamepad;
this.dalamud.ClientState.GamepadState.NavEnableGamepad ^= true;
this.dalamud.DalamudUi.ToggleGamePadNotifierWindow();
this.dalamud.DalamudUi.ToggleGamepadModeNotifierWindow();
}
if (gamepadEnabled
@ -592,7 +589,7 @@ namespace Dalamud.Interface
if (this.dalamud.ClientState.GamepadState.Pressed(GamepadButtons.R3) > 0)
{
this.dalamud.DalamudUi.TogglePluginInstaller();
this.dalamud.DalamudUi.TogglePluginInstallerWindow();
}
}
}

View file

@ -9,7 +9,7 @@ using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using Serilog;
namespace Dalamud.Interface.Scratchpad
namespace Dalamud.Interface.Internal.Scratchpad
{
/// <summary>
/// This class manages the execution of <see cref="ScratchpadDocument"/> classes.
@ -83,9 +83,9 @@ namespace Dalamud.Interface.Scratchpad
{
var script = CSharpScript.Create(code, options);
var pi = new DalamudPluginInterface(this.dalamud, "Scratch-" + doc.Id, null, PluginLoadReason.Installer);
var plugin = script.ContinueWith<IDalamudPlugin>("return new ScratchPlugin() as IDalamudPlugin;").RunAsync().GetAwaiter().GetResult()
.ReturnValue;
var pi = new DalamudPluginInterface(this.dalamud, "Scratch-" + doc.Id, null);
var plugin = script.ContinueWith<IDalamudPlugin>("return new ScratchPlugin() as IDalamudPlugin;")
.RunAsync().GetAwaiter().GetResult().ReturnValue;
plugin.Initialize(pi);

View file

@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.IO;
namespace Dalamud.Interface.Scratchpad
namespace Dalamud.Interface.Internal.Scratchpad
{
/// <summary>
/// A file watcher for <see cref="ScratchpadDocument"/> classes.

View file

@ -1,4 +1,4 @@
namespace Dalamud.Interface.Scratchpad
namespace Dalamud.Interface.Internal.Scratchpad
{
/// <summary>
/// The load status of a <see cref="ScratchpadDocument"/> class.

Some files were not shown because too many files have changed in this diff Show more