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_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = 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.severity = warning
dotnet_naming_rule.event_rule.style = on_upper_camel_case_style dotnet_naming_rule.event_rule.style = on_upper_camel_case_style
dotnet_naming_rule.event_rule.symbols = event_symbols 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_accessibilities = private
dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field
dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static,readonly 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_arithmetic_binary_operators =always_for_clarity:suggestion
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:suggestion dotnet_style_parentheses_in_other_binary_operators =always_for_clarity:suggestion
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion dotnet_style_predefined_type_for_member_access = true:suggestion
dotnet_style_require_accessibility_modifiers = for_non_interface_members: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 properties
resharper_align_linq_query = true 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_built_in_types_highlighting = hint
resharper_suggest_var_or_type_elsewhere_highlighting = hint resharper_suggest_var_or_type_elsewhere_highlighting = hint
resharper_suggest_var_or_type_simple_types_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}] [*.{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_style = space
indent_size = 4 indent_size = 4
tab_width = 4 tab_width = 4
dotnet_style_parentheses_in_other_operators=always_for_clarity:silent

View file

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

View file

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

3
.gitmodules vendored
View file

@ -4,3 +4,6 @@
[submodule "lib/FFXIVClientStructs"] [submodule "lib/FFXIVClientStructs"]
path = lib/FFXIVClientStructs path = lib/FFXIVClientStructs
url = https://github.com/goatcorp/FFXIVClientStructs.git 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 { Set-Location $args[0]
$hashes.Add($_.Name, (Get-FileHash $_.FullName -Algorithm MD5).Hash)
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"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Target"> <PropertyGroup Label="Target">
<PlatformTarget>AnyCPU</PlatformTarget> <TargetFramework>net5.0</TargetFramework>
<TargetFramework>net48</TargetFramework> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<LangVersion>8.0</LangVersion> <PlatformTarget>x64</PlatformTarget>
<Platforms>AnyCPU;x64</Platforms> <Platforms>x64;AnyCPU</Platforms>
</PropertyGroup> <LangVersion>9.0</LangVersion>
<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>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Label="Feature"> <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> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AssemblyVersion>5.2.4.6</AssemblyVersion> </PropertyGroup>
<FileVersion>5.2.4.6</FileVersion>
<Description>XIVLauncher addon injection</Description> <PropertyGroup Label="Configuration">
<Version>5.2.4.6</Version> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'"> <PropertyGroup Condition="'$(Configuration)'=='Release'">
<AppOutputBase>$(MSBuildProjectDirectory)\</AppOutputBase> <AppOutputBase>$(MSBuildProjectDirectory)\</AppOutputBase>
<PathMap>$(AppOutputBase)=C:\goatsoft\companysecrets\injector\</PathMap> <PathMap>$(AppOutputBase)=C:\goatsoft\companysecrets\injector\</PathMap>
<Deterministic>true</Deterministic>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <PropertyGroup Label="Warnings">
<PackageIcon></PackageIcon> <NoWarn>IDE1006;CS1591;CS1701;CS1702</NoWarn>
<PackageIconUrl /> <!-- IDE1006 - Naming violation -->
<ApplicationIcon>dalamud.ico</ApplicationIcon> <!-- CS1591 - Missing XML comment for publicly visible type or member -->
<!-- CS1701 - Runtime policy may be needed -->
<!-- CS1702 - Runtime policy may be needed -->
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="stylecop.json" /> <PackageReference Include="Iced" Version="1.12.0" />
</ItemGroup> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<ItemGroup> <PackageReference Include="PeNet" Version="2.6.3" />
<AdditionalFiles Include="stylecop.json" /> <PackageReference Include="Reloaded.Memory" Version="4.1.1" />
</ItemGroup> <PackageReference Include="Reloaded.Memory.Buffers" Version="1.3.5" />
<ItemGroup> <PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="EasyHook" Version="2.7.6270" /> <PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" /> <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.333"> <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.333">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\DalamudDebugStub\DalamudDebugStub.vcxproj" /> <AdditionalFiles Include="..\stylecop.json" />
<ProjectReference Include="..\Dalamud\Dalamud.csproj" />
</ItemGroup> </ItemGroup>
<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> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)'=='Release'">
<Exec Command="powershell -ExecutionPolicy Unrestricted $(SolutionDir)CreateHashList.ps1 $(OutputPath)" />
</Target>
</Project> </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; using System.Diagnostics.CodeAnalysis;
// General // 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.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
[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.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying", Scope = "namespaceanddescendants", Target = "~N:Dalamud")] [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", "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.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("Performance", "CA1822:Mark members as static", Justification = "I'll make what I want static", 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)")]

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;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security;
namespace Dalamud.Injector namespace Dalamud.Injector
{ {
/// <summary> /// <summary>
/// Native functions. /// Native user32 functions.
/// </summary> /// </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> /// <summary>
/// MEM_* from memoryapi. /// MEM_* from memoryapi.
@ -20,14 +240,14 @@ namespace Dalamud.Injector
/// To coalesce two adjacent placeholders, specify MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS. When you coalesce /// 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. /// placeholders, lpAddress and dwSize must exactly match those of the placeholder.
/// </summary> /// </summary>
CoalescePlaceholders = 0x00000001, CoalescePlaceholders = 0x1,
/// <summary> /// <summary>
/// Frees an allocation back to a placeholder (after you've replaced a placeholder with a private allocation using /// 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 /// VirtualAlloc2 or Virtual2AllocFromApp). To split a placeholder into two placeholders, specify
/// MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER. /// MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER.
/// </summary> /// </summary>
PreservePlaceholder = 0x00000002, PreservePlaceholder = 0x2,
/// <summary> /// <summary>
/// Allocates memory charges (from the overall size of memory and the paging files on disk) for the specified reserved /// Allocates memory charges (from the overall size of memory and the paging files on disk) for the specified reserved
@ -122,6 +342,28 @@ namespace Dalamud.Injector
LargePages = 0x20000000, 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> /// <summary>
/// PAGE_* from memoryapi. /// PAGE_* from memoryapi.
/// </summary> /// </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 /// The default behavior for VirtualProtect protection change to executable is to mark all locations as valid call
/// targets for CFG. /// targets for CFG.
/// </summary> /// </summary>
TargetsNoUpdate = 0x40000000, TargetsNoUpdate = TargetsInvalid,
/// <summary> /// <summary>
/// Pages in the region become guard pages. Any attempt to access a guard page causes the system to raise a /// 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> /// <summary>
/// Closes an open object handle. /// WAIT_* from synchapi.
/// </summary> /// </summary>
/// <param name="hObject"> public enum WaitResult
/// A valid handle to an open object. {
/// </param> /// <summary>
/// <returns> /// The specified object is a mutex object that was not released by the thread that owned the mutex object
/// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.To get extended /// before the owning thread terminated.Ownership of the mutex object is granted to the calling thread and
/// error information, call GetLastError. If the application is running under a debugger, the function will throw an /// the mutex state is set to nonsignaled. If the mutex was protecting persistent state information, you
/// exception if it receives either a handle value that is not valid or a pseudo-handle value. This can happen if you /// should check it for consistency.
/// close a handle twice, or if you call CloseHandle on a handle returned by the FindFirstFile function instead of calling /// </summary>
/// the FindClose function. Abandoned = 0x80,
/// </returns>
[DllImport("kernel32.dll", SetLastError = true)] /// <summary>
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] /// The state of the specified object is signaled.
[SuppressUnmanagedCodeSecurity] /// </summary>
[return: MarshalAs(UnmanagedType.Bool)] Object0 = 0x0,
public static extern bool CloseHandle(IntPtr hObject);
/// <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> /// <summary>
/// Creates a thread that runs in the virtual address space of another process. Use the CreateRemoteThreadEx function /// 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> /// </summary>
/// <param name="hProcess"> /// <param name="hProcess">
/// A handle to the process in which the thread is to be created. The handle must have the PROCESS_CREATE_THREAD, /// 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 /// PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ access rights, and may fail without
/// without these rights on certain platforms. For more information, see Process Security and Access Rights. /// these rights on certain platforms. For more information, see Process Security and Access Rights.
/// </param> /// </param>
/// <param name="lpThreadAttributes"> /// <param name="lpThreadAttributes">
/// A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new thread and determines /// A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new thread and determines whether
/// whether child processes can inherit the returned handle. If lpThreadAttributes is NULL, the thread gets a default /// child processes can inherit the returned handle. If lpThreadAttributes is NULL, the thread gets a default security descriptor
/// security descriptor and the handle cannot be inherited. The access control lists (ACL) in the default security descriptor /// and the handle cannot be inherited. The access control lists (ACL) in the default security descriptor for a thread come from
/// for a thread come from the primary token of the creator. /// the primary token of the creator.
/// </param> /// </param>
/// <param name="dwStackSize"> /// <param name="dwStackSize">
/// The initial size of the stack, in bytes. The system rounds this value to the nearest page. If this parameter is /// The initial size of the stack, in bytes. The system rounds this value to the nearest page. If this parameter is 0 (zero), the
/// 0 (zero), the new thread uses the default size for the executable. For more information, see Thread Stack Size. /// new thread uses the default size for the executable. For more information, see Thread Stack Size.
/// </param> /// </param>
/// <param name="lpStartAddress"> /// <param name="lpStartAddress">
/// A pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread and /// A pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread and represents the
/// represents the starting address of the thread in the remote process. The function must exist in the remote process. /// starting address of the thread in the remote process. The function must exist in the remote process. For more information,
/// For more information, see ThreadProc. /// see ThreadProc.
/// </param> /// </param>
/// <param name="lpParameter"> /// <param name="lpParameter">
/// A pointer to a variable to be passed to the thread function. /// 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. /// The flags that control the creation of the thread.
/// </param> /// </param>
/// <param name="lpThreadId"> /// <param name="lpThreadId">
/// A pointer to a variable that receives the thread identifier. If this parameter is NULL, the thread identifier is /// A pointer to a variable that receives the thread identifier. If this parameter is NULL, the thread identifier is not returned.
/// not returned.
/// </param> /// </param>
/// <returns> /// <returns>
/// If the function succeeds, the return value is a handle to the new thread. If the function fails, the return value /// If the function succeeds, the return value is a handle to the new thread. If the function fails, the return value is
/// is NULL.To get extended error information, call GetLastError. Note that CreateRemoteThread may succeed even if /// NULL.To get extended error information, call GetLastError. Note that CreateRemoteThread may succeed even if lpStartAddress
/// lpStartAddress points to data, code, or is not accessible. If the start address is invalid when the thread runs, /// points to data, code, or is not accessible. If the start address is invalid when the thread runs, an exception occurs, and
/// an exception occurs, and the thread terminates. Thread termination due to a invalid start address is handled as /// the thread terminates. Thread termination due to a invalid start address is handled as an error exit for the thread's process.
/// an error exit for the thread's process. This behavior is similar to the asynchronous nature of CreateProcess, where /// This behavior is similar to the asynchronous nature of CreateProcess, where the process is created even if it refers to
/// the process is created even if it refers to invalid or missing dynamic-link libraries (DLL). /// invalid or missing dynamic-link libraries (DLL).
/// </returns> /// </returns>
[DllImport("kernel32.dll")] [DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateRemoteThread( public static extern IntPtr CreateRemoteThread(
IntPtr hProcess, IntPtr hProcess,
IntPtr lpThreadAttributes, IntPtr lpThreadAttributes,
uint dwStackSize, UIntPtr dwStackSize,
IntPtr lpStartAddress, IntPtr lpStartAddress,
IntPtr lpParameter, IntPtr lpParameter,
uint dwCreationFlags, CreateThreadFlags dwCreationFlags,
IntPtr lpThreadId); out uint lpThreadId);
/// <summary> /// <summary>
/// See https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlew. /// Retrieves the termination status of the specified thread.
/// 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.
/// </summary> /// </summary>
/// <param name="lpModuleName"> /// <param name="hThread">
/// The name of the loaded module (either a .dll or .exe file). If the file name extension is omitted, the default library /// A handle to the thread. The handle must have the THREAD_QUERY_INFORMATION or THREAD_QUERY_LIMITED_INFORMATION
/// extension .dll is appended. The file name string can include a trailing point character (.) to indicate that the /// access right.For more information, see Thread Security and Access Rights.
/// module name has no extension. The string does not have to specify a path. When specifying a path, be sure to use /// </param>
/// backslashes (\), not forward slashes (/). The name is compared (case independently) to the names of modules currently /// <param name="lpExitCode">
/// mapped into the address space of the calling process. If this parameter is NULL, GetModuleHandle returns a handle /// A pointer to a variable to receive the thread termination status.
/// 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> /// </param>
/// <returns> /// <returns>
/// If the function succeeds, the return value is a handle to the specified module. If the function fails, the return /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get
/// value is NULL.To get extended error information, call GetLastError. /// 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.
/// </returns> /// </returns>
[DllImport("kernel32.dll", SetLastError = true)] [DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess( [return: MarshalAs(UnmanagedType.Bool)]
ProcessAccessFlags processAccess, public static extern bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode);
bool bInheritHandle,
int processId);
/// <summary> /// <summary>
/// See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex. /// See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex.
@ -530,6 +733,27 @@ namespace Dalamud.Injector
int dwSize, int dwSize,
AllocationType dwFreeType); 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> /// <summary>
/// Writes data to an area of memory in a specified process. The entire area to be written to must be accessible or /// 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. /// 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,23 +1,33 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project Sdk="Microsoft.NET.Sdk">
<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')" /> <PropertyGroup Label="Target">
<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')" /> <TargetFramework>net5.0-windows</TargetFramework>
<Import Project="..\packages\xunit.core.2.4.1\build\xunit.core.props" Condition="Exists('..\packages\xunit.core.2.4.1\build\xunit.core.props')" /> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <PlatformTarget>x64</PlatformTarget>
<PropertyGroup> <Platforms>x64;AnyCPU</Platforms>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <LangVersion>9.0</LangVersion>
<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>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <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> <DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
@ -26,8 +36,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Label="Configuration" Condition=" '$(Configuration)' == 'Release' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
@ -35,62 +44,27 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <ProjectReference Include="..\Dalamud\Dalamud.csproj" />
<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>
<ItemGroup> <ItemGroup>
<Compile Include="LocalizationTests.cs" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<Compile Include="Game\Text\Sanitizer\SanitizerTests.cs" /> <PackageReference Include="xunit" Version="2.4.1" />
<Compile Include="Properties\AssemblyInfo.cs" /> <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> </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>
</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> </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,25 +1,25 @@
// ReSharper disable StringLiteralTypo
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Xunit; using Xunit;
namespace Dalamud.Test.Game.Text.Sanitizer { // ReSharper disable StringLiteralTypo
namespace Dalamud.Test.Game.Text.Sanitizer
public class SanitizerTests { {
public class SanitizerTests
{
private global::Dalamud.Game.Text.Sanitizer.Sanitizer sanitizer; private global::Dalamud.Game.Text.Sanitizer.Sanitizer sanitizer;
[Theory] [Theory]
[InlineData( ClientLanguage.English, "Pixie Cotton Hood of Healing", "Pixie Cotton Hood of Healing" )] [InlineData(ClientLanguage.English, "Pixie Cotton Hood of Healing", "Pixie Cotton Hood of Healing")]
[InlineData( ClientLanguage.Japanese, "アラガントームストーン:真理", "アラガントームストーン:真理" )] [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, "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.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, "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" )] [InlineData(ClientLanguage.French, "Cuir de bœuf", "Cuir de boeuf")]
public void StringsAreSanitizedCorrectly( public void StringsAreSanitizedCorrectly(ClientLanguage clientLanguage, string unsanitizedString, string sanitizedString)
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); sanitizer = new global::Dalamud.Game.Text.Sanitizer.Sanitizer(clientLanguage);
Assert.Equal(sanitizedString, sanitizer.Sanitize(unsanitizedString)); Assert.Equal(sanitizedString, sanitizer.Sanitize(unsanitizedString));
Assert.Equal(sanitizedString, sanitizer.Sanitize(sanitizedStrings).First()); Assert.Equal(sanitizedString, sanitizer.Sanitize(sanitizedStrings).First());

View file

@ -1,20 +1,24 @@
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using Xunit; using Xunit;
namespace Dalamud.Test { namespace Dalamud.Test
public class LocalizationTests { {
public class LocalizationTests
{
private readonly Localization localization; private readonly Localization localization;
private string currentLangCode; private string currentLangCode;
public LocalizationTests() { public LocalizationTests()
{
var workingDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var workingDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
this.localization = new Localization(workingDir, "dalamud_"); this.localization = new Localization(workingDir, "dalamud_");
this.localization.OnLocalizationChanged += code => this.currentLangCode = code; this.localization.OnLocalizationChanged += code => this.currentLangCode = code;
} }
[Fact] [Fact]
public void SetupWithFallbacks_EventInvoked() { public void SetupWithFallbacks_EventInvoked()
{
this.localization.SetupWithFallbacks(); this.localization.SetupWithFallbacks();
Assert.Equal("en", this.currentLangCode); 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 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 17
VisualStudioVersion = 16.0.29215.179 VisualStudioVersion = 17.0.31410.414
MinimumVisualStudioVersion = 10.0.40219.1 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud", "Dalamud\Dalamud.csproj", "{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}"
EndProject 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.Injector", "Dalamud.Injector\Dalamud.Injector.csproj", "{5B832F73-5F54-4ADC-870F-D0095EF72C9A}"
EndProject 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 EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SDL2-CS", "lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj", "{85480198-8711-4355-830E-72FD794AD3F6}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.Test", "Dalamud.Test\Dalamud.Test.csproj", "{C8004563-1806-4329-844F-0EF6274291FC}"
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}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interface", "Interface", "{E15BDA6D-E881-4482-94BA-BE5527E917FF}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interface", "Interface", "{E15BDA6D-E881-4482-94BA-BE5527E917FF}"
EndProject 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 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 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 EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64 Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64 Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {94E5B016-02B1-459B-97D9-E783F28764B2}.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.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.ActiveCfg = Debug|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|x64.Build.0 = 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}.Release|Any CPU.ActiveCfg = Release|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|x86.Build.0 = Debug|Any CPU {B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|Any CPU.Build.0 = Release|x64
{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|x64.ActiveCfg = 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|x64.Build.0 = Release|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|x86.ActiveCfg = Release|Any CPU {55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|Any CPU.ActiveCfg = Debug|x64
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|x86.Build.0 = Release|Any CPU {55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|Any CPU.Build.0 = Debug|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|x64.ActiveCfg = Debug|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|Any CPU.Build.0 = Debug|Any CPU {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.ActiveCfg = Debug|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|x64.Build.0 = 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}.Release|Any CPU.ActiveCfg = Release|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|x86.Build.0 = Debug|Any CPU {5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|Any CPU.Build.0 = Release|x64
{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|x64.ActiveCfg = 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|x64.Build.0 = Release|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x86.ActiveCfg = Release|Any CPU {8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|Any CPU.ActiveCfg = Debug|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x86.Build.0 = Release|Any CPU {8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|x64.ActiveCfg = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|x64.Build.0 = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.Build.0 = Debug|Any CPU {8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|Any CPU.ActiveCfg = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x64.ActiveCfg = Debug|Any CPU {8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|x64.ActiveCfg = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x64.Build.0 = Debug|Any CPU {8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|x64.Build.0 = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x86.ActiveCfg = Debug|Any CPU {C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.ActiveCfg = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x86.Build.0 = Debug|Any CPU {C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.Build.0 = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.ActiveCfg = Release|Any CPU {C8004563-1806-4329-844F-0EF6274291FC}.Debug|x64.ActiveCfg = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.Build.0 = Release|Any CPU {C8004563-1806-4329-844F-0EF6274291FC}.Debug|x64.Build.0 = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x64.ActiveCfg = Release|Any CPU {C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.ActiveCfg = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x64.Build.0 = Release|Any CPU {C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.Build.0 = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x86.ActiveCfg = Release|Any CPU {C8004563-1806-4329-844F-0EF6274291FC}.Release|x64.ActiveCfg = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x86.Build.0 = Release|Any CPU {C8004563-1806-4329-844F-0EF6274291FC}.Release|x64.Build.0 = Release|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.ActiveCfg = Debug|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|Any CPU.Build.0 = Debug|Any CPU {0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.Build.0 = Debug|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|x64.ActiveCfg = Debug|x64 {0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.ActiveCfg = Debug|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|x64.Build.0 = Debug|x64 {0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.Build.0 = Debug|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|x86.ActiveCfg = Debug|x86 {0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.ActiveCfg = Release|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|x86.Build.0 = Debug|x86 {0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.Build.0 = Release|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Release|Any CPU.ActiveCfg = Release|Any CPU {0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.ActiveCfg = Release|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Release|Any CPU.Build.0 = Release|Any CPU {0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.Build.0 = Release|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Release|x64.ActiveCfg = Release|x64 {C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.ActiveCfg = Debug|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Release|x64.Build.0 = Release|x64 {C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.Build.0 = Debug|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Release|x86.ActiveCfg = Release|x86 {C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x64.ActiveCfg = Debug|x64
{85480198-8711-4355-830E-72FD794AD3F6}.Release|x86.Build.0 = Release|x86 {C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x64.Build.0 = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.ActiveCfg = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.Build.0 = Debug|Any CPU {C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.Build.0 = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.ActiveCfg = Debug|Any CPU {C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x64.ActiveCfg = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.Build.0 = Debug|Any CPU {C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x64.Build.0 = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x86.ActiveCfg = Debug|Any CPU {2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|Any CPU.ActiveCfg = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x86.Build.0 = Debug|Any CPU {2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|Any CPU.Build.0 = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.ActiveCfg = Release|Any CPU {2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|x64.ActiveCfg = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.Build.0 = Release|Any CPU {2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|x64.Build.0 = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.ActiveCfg = Release|Any CPU {2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|Any CPU.ActiveCfg = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.Build.0 = Release|Any CPU {2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|Any CPU.Build.0 = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x86.ActiveCfg = Release|Any CPU {2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|x64.ActiveCfg = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x86.Build.0 = Release|Any CPU {2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|x64.Build.0 = Release|x64
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Debug|Any CPU.ActiveCfg = Debug|x64 {4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Debug|x64.ActiveCfg = Debug|x64 {4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Debug|x86.ActiveCfg = Debug|Win32 {4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|x64.ActiveCfg = Debug|Any CPU
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|Any CPU.ActiveCfg = Release|x64 {4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|x64.Build.0 = Debug|Any CPU
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|Any CPU.Build.0 = Release|x64 {4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|x64.ActiveCfg = Release|x64 {4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|Any CPU.Build.0 = Release|Any CPU
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|x64.Build.0 = Release|x64 {4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|x64.ActiveCfg = Release|Any CPU
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|x86.ActiveCfg = Release|Win32 {4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|x64.Build.0 = Release|Any CPU
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|x86.Build.0 = Release|Win32 {C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|Any CPU.Build.0 = Debug|Any CPU {C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|x64.ActiveCfg = Debug|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|x64.ActiveCfg = Debug|Any CPU {C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|x64.Build.0 = Debug|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|x64.Build.0 = Debug|Any CPU {C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|x86.ActiveCfg = Debug|Any CPU {C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|Any CPU.Build.0 = Release|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|x86.Build.0 = Debug|Any CPU {C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|x64.ActiveCfg = Release|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|Any CPU.ActiveCfg = Release|Any CPU {C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|x64.Build.0 = Release|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|Any CPU.Build.0 = Release|Any CPU {F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|x64.ActiveCfg = Release|Any CPU {F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|x64.Build.0 = Release|Any CPU {F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Debug|x64.ActiveCfg = Debug|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|x86.ActiveCfg = Release|Any CPU {F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Debug|x64.Build.0 = Debug|Any CPU
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|x86.Build.0 = Release|Any CPU {F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Release|Any CPU.Build.0 = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.Build.0 = Debug|Any CPU {F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Release|x64.ActiveCfg = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x64.ActiveCfg = Debug|Any CPU {F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Release|x64.Build.0 = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x64.Build.0 = Debug|Any CPU {05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x86.ActiveCfg = Debug|Any CPU {05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x86.Build.0 = Debug|Any CPU {05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|x64.ActiveCfg = Debug|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.ActiveCfg = Release|Any CPU {05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|x64.Build.0 = Debug|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.Build.0 = Release|Any CPU {05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x64.ActiveCfg = Release|Any CPU {05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|Any CPU.Build.0 = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x64.Build.0 = Release|Any CPU {05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|x64.ActiveCfg = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x86.ActiveCfg = Release|Any CPU {05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|x64.Build.0 = Release|Any CPU
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution 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} {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 EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {79B65AC9-C940-410E-AB61-7EA7E12C7599} SolutionGuid = {79B65AC9-C940-410E-AB61-7EA7E12C7599}

View file

@ -7,19 +7,19 @@ using Newtonsoft.Json;
using Serilog; using Serilog;
using Serilog.Events; using Serilog.Events;
namespace Dalamud.Configuration namespace Dalamud.Configuration.Internal
{ {
/// <summary> /// <summary>
/// Class containing Dalamud settings. /// Class containing Dalamud settings.
/// </summary> /// </summary>
[Serializable] [Serializable]
public class DalamudConfiguration internal sealed class DalamudConfiguration
{ {
[JsonIgnore] [JsonIgnore]
private string configPath; private string configPath;
/// <summary> /// <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> /// </summary>
/// <param name="dalamudConfiguration">The current dalamud configuration.</param> /// <param name="dalamudConfiguration">The current dalamud configuration.</param>
public delegate void DalamudConfigurationSavedDelegate(DalamudConfiguration dalamudConfiguration); public delegate void DalamudConfigurationSavedDelegate(DalamudConfiguration dalamudConfiguration);
@ -69,15 +69,25 @@ namespace Dalamud.Configuration
/// </summary> /// </summary>
public bool DoDalamudTest { get; set; } 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> /// <summary>
/// Gets or sets a list of custom repos. /// Gets or sets a list of custom repos.
/// </summary> /// </summary>
public List<ThirdRepoSetting> ThirdRepoList { get; set; } = new List<ThirdRepoSetting>(); public List<ThirdPartyRepoSettings> ThirdRepoList { get; set; } = new();
/// <summary> /// <summary>
/// Gets or sets a list of hidden plugins. /// Gets or sets a list of hidden plugins.
/// </summary> /// </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> /// <summary>
/// Gets or sets the global UI scale. /// Gets or sets the global UI scale.
@ -150,7 +160,7 @@ namespace Dalamud.Configuration
public bool IsAntiAntiDebugEnabled { get; set; } = false; public bool IsAntiAntiDebugEnabled { get; set; } = false;
/// <summary> /// <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> /// </summary>
public string DalamudBetaKind { get; set; } 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> /// <summary>
/// Third party repository for dalamud plugins. /// Third party repository for dalamud plugins.
/// </summary> /// </summary>
public class ThirdRepoSetting internal sealed class ThirdPartyRepoSettings
{ {
/// <summary> /// <summary>
/// Gets or sets the third party repo url. /// Gets or sets the third party repo url.
@ -16,16 +16,14 @@ namespace Dalamud.Configuration
public bool IsEnabled { get; set; } public bool IsEnabled { get; set; }
/// <summary> /// <summary>
/// Create new instance of third party repo object. /// Gets or sets a short name for the repo url.
/// </summary> /// </summary>
/// <returns>New instance of third party repo.</returns> public string Name { get; set; }
public ThirdRepoSetting Clone()
{ /// <summary>
return new ThirdRepoSetting /// Clone this object.
{ /// </summary>
Url = this.Url, /// <returns>A shallow copy of this object.</returns>
IsEnabled = this.IsEnabled, public ThirdPartyRepoSettings Clone() => this.MemberwiseClone() as ThirdPartyRepoSettings;
};
}
} }
} }

View file

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

View file

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

View file

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

View file

@ -1,12 +1,15 @@
using System; using System;
using Dalamud.Game;
using Newtonsoft.Json;
namespace Dalamud namespace Dalamud
{ {
/// <summary> /// <summary>
/// Class containing information needed to initialize Dalamud. /// Struct containing information needed to initialize Dalamud.
/// </summary> /// </summary>
[Serializable] [Serializable]
public sealed class DalamudStartInfo public struct DalamudStartInfo
{ {
/// <summary> /// <summary>
/// The working directory of the XIVLauncher installations. /// The working directory of the XIVLauncher installations.
@ -41,7 +44,8 @@ namespace Dalamud
/// <summary> /// <summary>
/// The current game version code. /// The current game version code.
/// </summary> /// </summary>
public string GameVersion; [JsonConverter(typeof(GameVersionConverter))]
public GameVersion GameVersion;
/// <summary> /// <summary>
/// Whether or not market board information should be uploaded by default. /// 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.Data.LuminaExtensions;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Internal;
using ImGuiScene; using ImGuiScene;
using JetBrains.Annotations; using JetBrains.Annotations;
using Lumina; using Lumina;
@ -21,7 +22,7 @@ namespace Dalamud.Data
/// <summary> /// <summary>
/// This class provides data for Dalamud-internal features, but can also be used by plugins if needed. /// This class provides data for Dalamud-internal features, but can also be used by plugins if needed.
/// </summary> /// </summary>
public class DataManager : IDisposable public sealed class DataManager : IDisposable
{ {
private const string IconFileFormat = "ui/icon/{0:D3}000/{1}{2:D6}.tex"; private const string IconFileFormat = "ui/icon/{0:D3}000/{1}{2:D6}.tex";
private readonly InterfaceManager interfaceManager; private readonly InterfaceManager interfaceManager;
@ -32,6 +33,7 @@ namespace Dalamud.Data
private GameData gameData; private GameData gameData;
private Thread luminaResourceThread; private Thread luminaResourceThread;
private CancellationTokenSource luminaCancellationTokenSource;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DataManager"/> class. /// Initializes a new instance of the <see cref="DataManager"/> class.
@ -41,8 +43,9 @@ namespace Dalamud.Data
internal DataManager(ClientLanguage language, InterfaceManager interfaceManager) internal DataManager(ClientLanguage language, InterfaceManager interfaceManager)
{ {
this.interfaceManager = interfaceManager; this.interfaceManager = interfaceManager;
// Set up default values so plugins do not null-reference when data is being loaded. // 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; this.Language = language;
} }
@ -99,7 +102,7 @@ namespace Dalamud.Data
ClientLanguage.English => Lumina.Data.Language.English, ClientLanguage.English => Lumina.Data.Language.English,
ClientLanguage.German => Lumina.Data.Language.German, ClientLanguage.German => Lumina.Data.Language.German,
ClientLanguage.French => Lumina.Data.Language.French, 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); return this.Excel.GetSheet<T>(lang);
} }
@ -162,7 +165,7 @@ namespace Dalamud.Data
ClientLanguage.English => "en/", ClientLanguage.English => "en/",
ClientLanguage.German => "de/", ClientLanguage.German => "de/",
ClientLanguage.French => "fr/", 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); return this.GetIcon(type, iconId);
@ -232,7 +235,7 @@ namespace Dalamud.Data
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
this.luminaResourceThread.Abort(); this.luminaCancellationTokenSource.Cancel();
} }
/// <summary> /// <summary>
@ -245,14 +248,14 @@ namespace Dalamud.Data
{ {
Log.Verbose("Starting data load..."); Log.Verbose("Starting data load...");
var zoneOpCodeDict = var zoneOpCodeDict = JsonConvert.DeserializeObject<Dictionary<string, ushort>>(
JsonConvert.DeserializeObject<Dictionary<string, ushort>>(File.ReadAllText(Path.Combine(baseDir, "UIRes", "serveropcode.json"))); File.ReadAllText(Path.Combine(baseDir, "UIRes", "serveropcode.json")));
this.ServerOpCodes = new ReadOnlyDictionary<string, ushort>(zoneOpCodeDict); this.ServerOpCodes = new ReadOnlyDictionary<string, ushort>(zoneOpCodeDict);
Log.Verbose("Loaded {0} ServerOpCodes.", zoneOpCodeDict.Count); Log.Verbose("Loaded {0} ServerOpCodes.", zoneOpCodeDict.Count);
var clientOpCodeDict = var clientOpCodeDict = JsonConvert.DeserializeObject<Dictionary<string, ushort>>(
JsonConvert.DeserializeObject<Dictionary<string, ushort>>(File.ReadAllText(Path.Combine(baseDir, "UIRes", "clientopcode.json"))); File.ReadAllText(Path.Combine(baseDir, "UIRes", "clientopcode.json")));
this.ClientOpCodes = new ReadOnlyDictionary<string, ushort>(clientOpCodeDict); this.ClientOpCodes = new ReadOnlyDictionary<string, ushort>(clientOpCodeDict);
Log.Verbose("Loaded {0} ClientOpCodes.", clientOpCodeDict.Count); Log.Verbose("Loaded {0} ClientOpCodes.", clientOpCodeDict.Count);
@ -273,9 +276,7 @@ namespace Dalamud.Data
ClientLanguage.English => Lumina.Data.Language.English, ClientLanguage.English => Lumina.Data.Language.English,
ClientLanguage.German => Lumina.Data.Language.German, ClientLanguage.German => Lumina.Data.Language.German,
ClientLanguage.French => Lumina.Data.Language.French, ClientLanguage.French => Lumina.Data.Language.French,
_ => throw new ArgumentOutOfRangeException( _ => throw new ArgumentOutOfRangeException(nameof(this.Language), $"Unknown Language: {this.Language}"),
nameof(this.Language),
@"Unknown Language: " + this.Language),
}, },
}; };
@ -289,9 +290,12 @@ namespace Dalamud.Data
this.IsDataReady = true; 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) if (this.gameData.FileHandleManager.HasPendingFileLoads)
{ {
@ -302,8 +306,6 @@ namespace Dalamud.Data
Thread.Sleep(5); Thread.Sleep(5);
} }
} }
// ReSharper disable once FunctionNeverReturns
}); });
this.luminaResourceThread.Start(); this.luminaResourceThread.Start();
} }

View file

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

View file

@ -16,11 +16,11 @@ namespace Dalamud.Game.Addon
internal sealed unsafe partial class DalamudSystemMenu internal sealed unsafe partial class DalamudSystemMenu
{ {
private readonly Dalamud dalamud; private readonly Dalamud dalamud;
private AtkValueChangeType atkValueChangeType; private readonly AtkValueChangeType atkValueChangeType;
private AtkValueSetString atkValueSetString; private readonly AtkValueSetString atkValueSetString;
private Hook<AgentHudOpenSystemMenuPrototype> hookAgentHudOpenSystemMenu; private readonly Hook<AgentHudOpenSystemMenuPrototype> hookAgentHudOpenSystemMenu;
// TODO: Make this into events in Framework.Gui // TODO: Make this into events in Framework.Gui
private Hook<UiModuleRequestMainCommand> hookUiModuleRequestMainCommand; private readonly Hook<UiModuleRequestMainCommand> hookUiModuleRequestMainCommand;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DalamudSystemMenu"/> class. /// 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); this.hookAgentHudOpenSystemMenu = new Hook<AgentHudOpenSystemMenuPrototype>(openSystemMenuAddress, this.AgentHudOpenSystemMenuDetour);
var atkValueChangeTypeAddress = var atkValueChangeTypeAddress = this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 45 84 F6 48 8D 4C 24 ??");
this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 45 84 F6 48 8D 4C 24 ??"); this.atkValueChangeType = Marshal.GetDelegateForFunctionPointer<AtkValueChangeType>(atkValueChangeTypeAddress);
this.atkValueChangeType =
Marshal.GetDelegateForFunctionPointer<AtkValueChangeType>(atkValueChangeTypeAddress);
var atkValueSetStringAddress = var atkValueSetStringAddress = this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 41 03 ED");
this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 41 03 ED");
this.atkValueSetString = Marshal.GetDelegateForFunctionPointer<AtkValueSetString>(atkValueSetStringAddress); 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 ?? ?? ?? ??"); 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) switch (commandId)
{ {
case 69420: case 69420:
this.dalamud.DalamudUi.TogglePluginInstaller(); this.dalamud.DalamudUi.TogglePluginInstallerWindow();
break; break;
case 69421: case 69421:
this.dalamud.DalamudUi.ToggleSettings(); this.dalamud.DalamudUi.ToggleSettingsWindow();
break; break;
default: default:
this.hookUiModuleRequestMainCommand.Original(thisPtr, commandId); this.hookUiModuleRequestMainCommand.Original(thisPtr, commandId);

View file

@ -11,7 +11,7 @@ using CheapLoc;
using Dalamud.Game.Text; using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Interface; using Dalamud.Interface.Internal.Windows;
using Serilog; using Serilog;
namespace Dalamud.Game namespace Dalamud.Game
@ -21,39 +21,39 @@ namespace Dalamud.Game
/// </summary> /// </summary>
public class ChatHandlers public class ChatHandlers
{ {
private static readonly Dictionary<string, string> UnicodeToDiscordEmojiDict = new() // private static readonly Dictionary<string, string> UnicodeToDiscordEmojiDict = new()
{ // {
{ "", "<:ffxive071:585847382210642069>" }, // { "", "<:ffxive071:585847382210642069>" },
{ "", "<:ffxive083:585848592699490329>" }, // { "", "<:ffxive083:585848592699490329>" },
}; // };
private readonly Dictionary<XivChatType, Color> handledChatTypeColors = new() // private readonly Dictionary<XivChatType, Color> handledChatTypeColors = new()
{ // {
{ XivChatType.CrossParty, Color.DodgerBlue }, // { XivChatType.CrossParty, Color.DodgerBlue },
{ XivChatType.Party, Color.DodgerBlue }, // { XivChatType.Party, Color.DodgerBlue },
{ XivChatType.FreeCompany, Color.DeepSkyBlue }, // { XivChatType.FreeCompany, Color.DeepSkyBlue },
{ XivChatType.CrossLinkShell1, Color.ForestGreen }, // { XivChatType.CrossLinkShell1, Color.ForestGreen },
{ XivChatType.CrossLinkShell2, Color.ForestGreen }, // { XivChatType.CrossLinkShell2, Color.ForestGreen },
{ XivChatType.CrossLinkShell3, Color.ForestGreen }, // { XivChatType.CrossLinkShell3, Color.ForestGreen },
{ XivChatType.CrossLinkShell4, Color.ForestGreen }, // { XivChatType.CrossLinkShell4, Color.ForestGreen },
{ XivChatType.CrossLinkShell5, Color.ForestGreen }, // { XivChatType.CrossLinkShell5, Color.ForestGreen },
{ XivChatType.CrossLinkShell6, Color.ForestGreen }, // { XivChatType.CrossLinkShell6, Color.ForestGreen },
{ XivChatType.CrossLinkShell7, Color.ForestGreen }, // { XivChatType.CrossLinkShell7, Color.ForestGreen },
{ XivChatType.CrossLinkShell8, Color.ForestGreen }, // { XivChatType.CrossLinkShell8, Color.ForestGreen },
{ XivChatType.Ls1, Color.ForestGreen }, // { XivChatType.Ls1, Color.ForestGreen },
{ XivChatType.Ls2, Color.ForestGreen }, // { XivChatType.Ls2, Color.ForestGreen },
{ XivChatType.Ls3, Color.ForestGreen }, // { XivChatType.Ls3, Color.ForestGreen },
{ XivChatType.Ls4, Color.ForestGreen }, // { XivChatType.Ls4, Color.ForestGreen },
{ XivChatType.Ls5, Color.ForestGreen }, // { XivChatType.Ls5, Color.ForestGreen },
{ XivChatType.Ls6, Color.ForestGreen }, // { XivChatType.Ls6, Color.ForestGreen },
{ XivChatType.Ls7, Color.ForestGreen }, // { XivChatType.Ls7, Color.ForestGreen },
{ XivChatType.Ls8, Color.ForestGreen }, // { XivChatType.Ls8, Color.ForestGreen },
{ XivChatType.TellIncoming, Color.HotPink }, // { XivChatType.TellIncoming, Color.HotPink },
{ XivChatType.PvPTeam, Color.SandyBrown }, // { XivChatType.PvPTeam, Color.SandyBrown },
{ XivChatType.Urgent, Color.DarkViolet }, // { XivChatType.Urgent, Color.DarkViolet },
{ XivChatType.NoviceNetwork, Color.SaddleBrown }, // { XivChatType.NoviceNetwork, Color.SaddleBrown },
{ XivChatType.Echo, Color.Gray }, // { XivChatType.Echo, Color.Gray },
}; // };
private readonly Regex rmtRegex = new( 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", @"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 Regex urlRegex = new(@"(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?", RegexOptions.Compiled);
private readonly Dalamud dalamud; private readonly Dalamud dalamud;
private DalamudLinkPayload openInstallerWindowLink; private readonly DalamudLinkPayload openInstallerWindowLink;
private bool hasSeenLoadingMsg; private bool hasSeenLoadingMsg;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ChatHandlers"/> class. /// Initializes a new instance of the <see cref="ChatHandlers"/> class.
/// </summary> /// </summary>
/// <param name="dalamud">Dalamud instance.</param> /// <param name="dalamud">Dalamud instance.</param>
public ChatHandlers(Dalamud dalamud) internal ChatHandlers(Dalamud dalamud)
{ {
this.dalamud = dalamud; this.dalamud = dalamud;
@ -121,24 +121,24 @@ namespace Dalamud.Game
/// </summary> /// </summary>
public string LastLink { get; private set; } public string LastLink { get; private set; }
/// <summary> // /// <summary>
/// Convert a string to SeString and wrap in italics payloads. // /// Convert a string to SeString and wrap in italics payloads.
/// </summary> // /// </summary>
/// <param name="text">Text to convert.</param> // /// <param name="text">Text to convert.</param>
/// <returns>SeString payload of italicized text.</returns> // /// <returns>SeString payload of italicized text.</returns>
private static SeString MakeItalics(string text) // private static SeString MakeItalics(string text)
{ // {
// TODO: when the code OnCharMessage is switched to SeString, this can be a straight insertion of the // // 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 // // italics payloads only, and be a lot cleaner
var italicString = new SeString(new List<Payload>(new Payload[] // var italicString = new SeString(new List<Payload>(new Payload[]
{ // {
EmphasisItalicPayload.ItalicsOn, // EmphasisItalicPayload.ItalicsOn,
new TextPayload(text), // new TextPayload(text),
EmphasisItalicPayload.ItalicsOff, // EmphasisItalicPayload.ItalicsOff,
})); // }));
//
return italicString; // return italicString;
} // }
private void OnCheckMessageHandled(XivChatType type, uint senderid, ref SeString sender, ref SeString message, ref bool isHandled) 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(); 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) 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) 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, Type = XivChatType.Notice,
}); });
if (DalamudChangelogWindow.WarrantsChangelog) if (this.dalamud.DalamudUi.WarrantsChangelog)
#pragma warning disable CS0162 // Unreachable code detected this.dalamud.DalamudUi.OpenChangelogWindow();
this.dalamud.DalamudUi.OpenChangelog();
#pragma warning restore CS0162 // Unreachable code detected
this.dalamud.Configuration.LastVersion = assemblyVersion; this.dalamud.Configuration.LastVersion = assemblyVersion;
this.dalamud.Configuration.Save(); 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) if (t.IsFaulted)
{ {
@ -278,13 +277,13 @@ namespace Dalamud.Game
} }
else else
{ {
var updatedPlugins = t.Result.UpdatedPlugins; var updatedPlugins = t.Result;
if (updatedPlugins != null && updatedPlugins.Any()) if (updatedPlugins != null && updatedPlugins.Any())
{ {
if (this.dalamud.Configuration.AutoUpdatePlugins) 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 else
{ {

View file

@ -34,14 +34,14 @@ namespace Dalamud.Game.ClientState.Actors
/// </summary> /// </summary>
/// <param name="dalamud">The Dalamud instance.</param> /// <param name="dalamud">The Dalamud instance.</param>
/// <param name="addressResolver">The ClientStateAddressResolver 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.address = addressResolver;
this.dalamud = dalamud; this.dalamud = dalamud;
dalamud.Framework.OnUpdateEvent += this.Framework_OnUpdateEvent; 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> /// <summary>

View file

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

View file

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

View file

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

View file

@ -1,7 +1,6 @@
using System; using System;
using System.Text;
using Dalamud.Game.ClientState.Structs; using Dalamud.Game.ClientState.Structs;
using Serilog;
namespace Dalamud.Game.ClientState.Actors.Types 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="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="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
/// <param name="address">The address of this actor in memory.</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.actorStruct = actorStruct;
this.dalamud = dalamud; this.dalamud = dalamud;
@ -94,7 +93,7 @@ namespace Dalamud.Game.ClientState.Actors.Types
/// <summary> /// <summary>
/// Gets the <see cref="Dalamud"/> backing instance. /// Gets the <see cref="Dalamud"/> backing instance.
/// </summary> /// </summary>
protected Dalamud Dalamud => this.dalamud; internal Dalamud Dalamud => this.dalamud;
/// <inheritdoc/> /// <inheritdoc/>
bool IEquatable<Actor>.Equals(Actor other) => this.ActorId == other.ActorId; 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="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="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
/// <param name="address">The address of this actor in memory.</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) : 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="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="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
/// <param name="address">The address of this actor in memory.</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) : 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="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="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
/// <param name="address">The address of this actor in memory.</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) : 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="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="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
/// <param name="address">The address of this actor in memory.</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) : 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="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="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
/// <param name="address">The address of this actor in memory.</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) : base(address, actorStruct, dalamud)
{ {
var companyTagBytes = new byte[5]; var companyTagBytes = new byte[5];

View file

@ -15,7 +15,7 @@ namespace Dalamud.Game.ClientState
/// <summary> /// <summary>
/// This class represents the state of the game client at the time of access. /// This class represents the state of the game client at the time of access.
/// </summary> /// </summary>
public class ClientState : INotifyPropertyChanged, IDisposable public sealed class ClientState : INotifyPropertyChanged, IDisposable
{ {
/// <summary> /// <summary>
/// The table of all present actors. /// The table of all present actors.
@ -80,7 +80,7 @@ namespace Dalamud.Game.ClientState
/// <param name="dalamud">Dalamud instance.</param> /// <param name="dalamud">Dalamud instance.</param>
/// <param name="startInfo">StartInfo of the current Dalamud launch.</param> /// <param name="startInfo">StartInfo of the current Dalamud launch.</param>
/// <param name="scanner">Sig scanner.</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.dalamud = dalamud;
this.address = new ClientStateAddressResolver(); this.address = new ClientStateAddressResolver();
@ -104,7 +104,7 @@ namespace Dalamud.Game.ClientState
this.Targets = new Targets(dalamud, this.address); 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); 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.Game.ClientState.Structs;
using Dalamud.Hooking; using Dalamud.Hooking;
@ -12,7 +12,7 @@ namespace Dalamud.Game.ClientState
/// ///
/// Will block game's gamepad input if <see cref="ImGuiConfigFlags.NavEnableGamepad"/> is set. /// Will block game's gamepad input if <see cref="ImGuiConfigFlags.NavEnableGamepad"/> is set.
/// </summary> /// </summary>
public unsafe class GamepadState public unsafe class GamepadState : IDisposable
{ {
private readonly Hook<ControllerPoll> gamepadPoll; 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> /// <param name="resolver">Resolver knowing the pointer to the GamepadPoll function.</param>
public GamepadState(ClientStateAddressResolver resolver) public GamepadState(ClientStateAddressResolver resolver)
{ {
#if DEBUG Log.Verbose($"GamepadPoll address 0x{resolver.GamepadPoll.ToInt64():X}");
Log.Verbose("GamepadPoll address {GamepadPoll}", resolver.GamepadPoll); this.gamepadPoll = new Hook<ControllerPoll>(resolver.GamepadPoll, this.GamepadPollDetour);
#endif
this.gamepadPoll = new Hook<ControllerPoll>(
resolver.GamepadPoll,
(ControllerPoll)this.GamepadPollDetour);
} }
/// <summary> /// <summary>

View file

@ -17,7 +17,7 @@ namespace Dalamud.Game.ClientState
{ {
this.Address = addressResolver; 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; } private ClientStateAddressResolver Address { get; }

View file

@ -25,7 +25,7 @@ namespace Dalamud.Game.ClientState
{ {
this.bufferBase = moduleBaseAddress + Marshal.ReadInt32(addressResolver.KeyboardState); 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> /// <summary>

View file

@ -25,7 +25,7 @@ namespace Dalamud.Game.ClientState
/// </summary> /// </summary>
/// <param name="dalamud">The Dalamud instance.</param> /// <param name="dalamud">The Dalamud instance.</param>
/// <param name="addressResolver">The ClientStateAddressResolver 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.address = addressResolver;
this.dalamud = dalamud; this.dalamud = dalamud;

View file

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

View file

@ -27,7 +27,7 @@ namespace Dalamud.Game.Command
/// </summary> /// </summary>
/// <param name="dalamud">The Dalamud instance.</param> /// <param name="dalamud">The Dalamud instance.</param>
/// <param name="language">The client language requested.</param> /// <param name="language">The client language requested.</param>
public CommandManager(Dalamud dalamud, ClientLanguage language) internal CommandManager(Dalamud dalamud, ClientLanguage language)
{ {
this.dalamud = dalamud; this.dalamud = dalamud;
@ -128,7 +128,8 @@ namespace Dalamud.Game.Command
/// <returns>If adding was successful.</returns> /// <returns>If adding was successful.</returns>
public bool AddHandler(string command, CommandInfo info) 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 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; this.debugCheckAddress = IntPtr.Zero;
} }
Log.Verbose("DebugCheck address {DebugCheckAddress}", this.debugCheckAddress); Log.Verbose($"Debug check address 0x{this.debugCheckAddress.ToInt64():X}");
} }
/// <summary> /// <summary>
@ -45,7 +45,7 @@ namespace Dalamud.Game.Internal
this.original = new byte[this.nop.Length]; this.original = new byte[this.nop.Length];
if (this.debugCheckAddress != IntPtr.Zero && !this.IsEnabled) 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.ReadBytes(this.debugCheckAddress, this.nop.Length, out this.original);
SafeMemory.WriteBytes(this.debugCheckAddress, this.nop); SafeMemory.WriteBytes(this.debugCheckAddress, this.nop);
} }
@ -64,7 +64,7 @@ namespace Dalamud.Game.Internal
{ {
if (this.debugCheckAddress != IntPtr.Zero && this.original != null) 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); SafeMemory.WriteBytes(this.debugCheckAddress, this.original);
} }
else 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 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 // 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. // check in either situation anyways. However if Dalamud is being reloaded, the sig may fail so may as well undo it.
// this.Disable(); 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"); 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); var scanner = new SigScanner(module);

View file

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

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using Dalamud.Game.Internal.Gui; using Dalamud.Game.Internal.Gui;
using Dalamud.Game.Internal.Libc; using Dalamud.Game.Internal.Libc;
@ -29,13 +30,13 @@ namespace Dalamud.Game.Internal
/// </summary> /// </summary>
/// <param name="scanner">The SigScanner instance.</param> /// <param name="scanner">The SigScanner instance.</param>
/// <param name="dalamud">The Dalamud 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.dalamud = dalamud;
this.Address = new FrameworkAddressResolver(); this.Address = new FrameworkAddressResolver();
this.Address.Setup(scanner); 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) if (this.Address.BaseAddress == IntPtr.Zero)
{ {
throw new InvalidOperationException("Framework is not initalized yet."); throw new InvalidOperationException("Framework is not initalized yet.");
@ -143,6 +144,11 @@ namespace Dalamud.Game.Internal
this.Gui.Dispose(); this.Gui.Dispose();
this.Network.Dispose(); this.Network.Dispose();
this.updateHook.Disable();
this.destroyHook.Disable();
this.realDestroyHook.Disable();
Thread.Sleep(500);
this.updateHook.Dispose(); this.updateHook.Dispose();
this.destroyHook.Dispose(); this.destroyHook.Dispose();
this.realDestroyHook.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] // 00007FF701AD666A | 48 8D ?? ?? ?? 00 00 | LEA RCX,QWORD PTR DS:[RBX + 2C38]
// 00007FF701AD6671 | E8 ?? ?? ?? ?? | CALL ffxiv_dx11.7FF701E2A7D0 // 00007FF701AD6671 | E8 ?? ?? ?? ?? | CALL ffxiv_dx11.7FF701E2A7D0
// 00007FF701AD6676 | 48 8D ?? ?? ?? ?? ?? | LEA RAX,QWORD PTR DS:[7FF702C31F80 // 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 fwOffset = Marshal.ReadInt32(fwDtor + 3);
var pFramework = scanner.ResolveRelativeAddress(fwDtor + 11, fwOffset); 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="baseAddress">The base address of the ChatManager.</param>
/// <param name="scanner">The SigScanner instance.</param> /// <param name="scanner">The SigScanner instance.</param>
/// <param name="dalamud">The Dalamud 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.dalamud = dalamud;
this.address = new ChatGuiAddressResolver(baseAddress); this.address = new ChatGuiAddressResolver(baseAddress);
this.address.Setup(scanner); 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.printMessageHook = new Hook<PrintMessageDelegate>(this.address.PrintMessage, this.HandlePrintMessageDetour);
this.populateItemLinkHook = new Hook<PopulateItemLinkDelegate>(this.address.PopulateItemLinkObject, this.HandlePopulateItemLinkDetour); 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); var senderRaw = Encoding.UTF8.GetBytes(chat.Name ?? string.Empty);
using var senderOwned = framework.Libc.NewString(senderRaw); 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); using var messageOwned = framework.Libc.NewString(messageRaw);
this.HandlePrintMessageDetour(this.baseAddress, chat.Type, senderOwned.Address, messageOwned.Address, chat.SenderId, chat.Parameters); 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> /// <summary>
/// Initializes a new instance of the <see cref="ChatGuiAddressResolver"/> class. /// Initializes a new instance of the <see cref="ChatGuiAddressResolver"/> class.
/// </summary> /// </summary>
/// <param name="baseAddres">The base address of the native ChatManager class.</param> /// <param name="baseAddress">The base address of the native ChatManager class.</param>
public ChatGuiAddressResolver(IntPtr baseAddres) public ChatGuiAddressResolver(IntPtr baseAddress)
{ {
this.BaseAddress = baseAddres; this.BaseAddress = baseAddress;
} }
/// <summary> /// <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="baseAddress">The base address of the native GuiManager class.</param>
/// <param name="scanner">The SigScanner instance.</param> /// <param name="scanner">The SigScanner instance.</param>
/// <param name="dalamud">The Dalamud 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; this.dalamud = dalamud;
@ -56,12 +56,12 @@ namespace Dalamud.Game.Internal.Gui
Log.Verbose("===== G A M E G U I ====="); Log.Verbose("===== G A M E G U I =====");
Log.Verbose("GameGuiManager address {Address:X}", this.address.BaseAddress.ToInt64()); Log.Verbose($"GameGuiManager address 0x{this.address.BaseAddress.ToInt64():X}");
Log.Verbose("SetGlobalBgm address {Address:X}", this.address.SetGlobalBgm.ToInt64()); Log.Verbose($"SetGlobalBgm address 0x{this.address.SetGlobalBgm.ToInt64():X}");
Log.Verbose("HandleItemHover address {Address:X}", this.address.HandleItemHover.ToInt64()); Log.Verbose($"HandleItemHover address 0x{this.address.HandleItemHover.ToInt64():X}");
Log.Verbose("HandleItemOut address {Address:X}", this.address.HandleItemOut.ToInt64()); Log.Verbose($"HandleItemOut address 0x{this.address.HandleItemOut.ToInt64():X}");
Log.Verbose("GetUIObject address {Address:X}", this.address.GetUIObject.ToInt64()); Log.Verbose($"GetUIObject address 0x{this.address.GetUIObject.ToInt64():X}");
Log.Verbose("GetAgentModule address {Address:X}", this.address.GetAgentModule.ToInt64()); Log.Verbose($"GetAgentModule address 0x{this.address.GetAgentModule.ToInt64():X}");
this.Chat = new ChatGui(this.address.ChatManager, scanner, dalamud); this.Chat = new ChatGui(this.address.ChatManager, scanner, dalamud);
this.PartyFinder = new PartyFinderGui(scanner, dalamud); this.PartyFinder = new PartyFinderGui(scanner, dalamud);

View file

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

View file

@ -1,5 +1,7 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using FFXIVClientStructs.FFXIV.Component.GUI;
namespace Dalamud.Game.Internal.Gui.Structs namespace Dalamud.Game.Internal.Gui.Structs
{ {
/// <summary> /// <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> /// </summary>
/// <param name="scanner">The SigScanner instance.</param> /// <param name="scanner">The SigScanner instance.</param>
/// <param name="dalamud">The Dalamud instance.</param> /// <param name="dalamud">The Dalamud instance.</param>
public ToastGui(SigScanner scanner, Dalamud dalamud) internal ToastGui(SigScanner scanner, Dalamud dalamud)
{ {
this.dalamud = dalamud; this.dalamud = dalamud;

View file

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

View file

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

View file

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

View file

@ -34,8 +34,8 @@ namespace Dalamud.Game
if (this.IsCopy) if (this.IsCopy)
this.SetupCopiedSegments(); this.SetupCopiedSegments();
Log.Verbose("Module base: {Address}", this.TextSectionBase); Log.Verbose($"Module base: 0x{this.TextSectionBase.ToInt64():X}");
Log.Verbose("Module size: {Size}", this.TextSectionSize); Log.Verbose($"Module size: 0x{this.TextSectionSize:X}");
} }
/// <summary> /// <summary>
@ -206,7 +206,7 @@ namespace Dalamud.Game
var insnByte = Marshal.ReadByte(scanRet); var insnByte = Marshal.ReadByte(scanRet);
if (insnByte == 0xE8 || insnByte == 0xE9) if (insnByte == 0xE8 || insnByte == 0xE9)
return ReadCallSig(scanRet); return ReadJmpCallSig(scanRet);
return scanRet; return scanRet;
} }
@ -220,13 +220,13 @@ namespace Dalamud.Game
} }
/// <summary> /// <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> /// </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> /// <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); return IntPtr.Add(sigLocation, 5 + jumpOffset);
} }
@ -235,6 +235,7 @@ namespace Dalamud.Game
signature = signature.Replace(" ", string.Empty); signature = signature.Replace(" ", string.Empty);
if (signature.Length % 2 != 0) if (signature.Length % 2 != 0)
throw new ArgumentException("Signature without whitespaces must be divisible by two.", nameof(signature)); throw new ArgumentException("Signature without whitespaces must be divisible by two.", nameof(signature));
var needleLength = signature.Length / 2; var needleLength = signature.Length / 2;
var needle = new byte[needleLength]; var needle = new byte[needleLength];
var mask = new bool[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> /// <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) 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> /// <summary>

View file

@ -18,26 +18,15 @@ using System.Diagnostics.CodeAnalysis;
// Extensions // 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.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.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.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.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.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.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.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")] [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 // DalamudStartInfo.cs
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Interop", Scope = "type", Target = "~T:Dalamud.DalamudStartInfo")] [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 // 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.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")] [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.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.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.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 = "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", "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.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")] [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("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("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("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.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.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. // 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")] [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.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using EasyHook; using CoreHook;
using Dalamud.Hooking.Internal;
namespace Dalamud.Hooking namespace Dalamud.Hooking
{ {
@ -30,8 +31,8 @@ namespace Dalamud.Hooking
{ {
this.hookInfo = LocalHook.Create(address, detour, null); // Installs a hook here this.hookInfo = LocalHook.Create(address, detour, null); // Installs a hook here
this.address = address; this.address = address;
this.original = Marshal.GetDelegateForFunctionPointer<T>(this.hookInfo.HookBypassAddress); this.original = Marshal.GetDelegateForFunctionPointer<T>(this.hookInfo.OriginalAddress);
HookInfo.TrackedHooks.Add(new HookInfo() { Delegate = detour, Hook = this, Assembly = Assembly.GetCallingAssembly() }); HookManager.TrackedHooks.Add(new HookInfo() { Delegate = detour, Hook = this, Assembly = Assembly.GetCallingAssembly() });
} }
/// <summary> /// <summary>
@ -130,9 +131,8 @@ namespace Dalamud.Hooking
return; return;
} }
this.hookInfo.Dispose();
this.IsDisposed = true; this.IsDisposed = true;
this.hookInfo.Dispose();
} }
/// <summary> /// <summary>

View file

@ -3,18 +3,13 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
namespace Dalamud.Hooking namespace Dalamud.Hooking.Internal
{ {
/// <summary> /// <summary>
/// Class containing information about registered hooks. /// Class containing information about registered hooks.
/// </summary> /// </summary>
internal class HookInfo internal class HookInfo
{ {
/// <summary>
/// Static list of tracked and registered hooks.
/// </summary>
internal static readonly List<HookInfo> TrackedHooks = new();
private ulong? inProcessMemory = 0; private ulong? inProcessMemory = 0;
/// <summary> /// <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; using System;
namespace Dalamud.Hooking namespace Dalamud.Hooking.Internal
{ {
/// <summary> /// <summary>
/// Interface describing a generic hook. /// 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> /// </summary>
public static partial class ImGuiComponents 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> /// <summary>
/// IconButton component to use an icon as a button. /// IconButton component to use an icon as a button.
/// </summary> /// </summary>
@ -16,16 +24,26 @@ namespace Dalamud.Interface.Components
/// <param name="icon">The icon for the button.</param> /// <param name="icon">The icon for the button.</param>
/// <returns>Indicator if button is clicked.</returns> /// <returns>Indicator if button is clicked.</returns>
public static bool IconButton(int id, FontAwesomeIcon icon) public static bool IconButton(int id, FontAwesomeIcon icon)
{ => IconButton(id, icon, null, null, null);
ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero);
ImGui.PushStyleColor(ImGuiCol.ButtonActive, Vector4.Zero); /// <summary>
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, Vector4.Zero); /// IconButton component to use an icon as a button.
ImGui.PushFont(UiBuilder.IconFont); /// </summary>
var button = ImGui.Button($"{icon.ToIconString()}{id}"); /// <param name="iconText">Text already containing the icon string.</param>
ImGui.PopFont(); /// <returns>Indicator if button is clicked.</returns>
ImGui.PopStyleColor(3); public static bool IconButton(string iconText)
return button; => 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> /// <summary>
/// IconButton component to use an icon as a button with color options. /// 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="activeColor">The color of the button when active.</param>
/// <param name="hoveredColor">The color of the button when hovered.</param> /// <param name="hoveredColor">The color of the button when hovered.</param>
/// <returns>Indicator if button is clicked.</returns> /// <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); var numColors = 0;
ImGui.PushStyleColor(ImGuiCol.ButtonActive, activeColor);
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, hoveredColor); 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); ImGui.PushFont(UiBuilder.IconFont);
var button = ImGui.Button($"{icon.ToIconString()}{id}");
var button = ImGui.Button(iconText);
ImGui.PopFont(); ImGui.PopFont();
ImGui.PopStyleColor(3);
if (numColors > 0)
ImGui.PopStyleColor(numColors);
return button; return button;
} }
} }

View file

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

View file

@ -20,6 +20,24 @@ namespace Dalamud.Interface
/// </summary> /// </summary>
public static float GlobalScale { get; private set; } 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> /// <summary>
/// Force the next ImGui window to stay inside the main game window. /// Force the next ImGui window to stay inside the main game window.
/// </summary> /// </summary>
@ -29,7 +47,14 @@ namespace Dalamud.Interface
/// Create a dummy scaled by the global Dalamud scale. /// Create a dummy scaled by the global Dalamud scale.
/// </summary> /// </summary>
/// <param name="size">The size of the dummy.</param> /// <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> /// <summary>
/// Create a dummy scaled by the global Dalamud scale. /// Create a dummy scaled by the global Dalamud scale.

View file

@ -7,7 +7,7 @@ using CheapLoc;
using Dalamud.Game.Command; using Dalamud.Game.Command;
using Serilog; using Serilog;
namespace Dalamud namespace Dalamud.Interface.Internal
{ {
/// <summary> /// <summary>
/// Class handling Dalamud core commands. /// Class handling Dalamud core commands.
@ -149,7 +149,7 @@ namespace Dalamud
try try
{ {
this.dalamud.PluginManager.ReloadPlugins(); this.dalamud.PluginManager.ReloadAllPlugins();
this.dalamud.Framework.Gui.Chat.Print("OK"); this.dalamud.Framework.Gui.Chat.Print("OK");
} }
@ -226,20 +226,20 @@ namespace Dalamud
private void OnDebugDrawDevMenu(string command, string arguments) 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) private void OnDebugDrawDataMenu(string command, string arguments)
{ {
if (string.IsNullOrEmpty(arguments)) if (string.IsNullOrEmpty(arguments))
this.dalamud.DalamudUi.ToggleData(); this.dalamud.DalamudUi.ToggleDataWindow();
else else
this.dalamud.DalamudUi.ToggleData(arguments); this.dalamud.DalamudUi.ToggleDataWindow(arguments);
} }
private void OnOpenLog(string command, string arguments) private void OnOpenLog(string command, string arguments)
{ {
this.dalamud.DalamudUi.ToggleLog(); this.dalamud.DalamudUi.ToggleLogWindow();
} }
private void OnDebugImInfoCommand(string command, string arguments) private void OnDebugImInfoCommand(string command, string arguments)
@ -267,12 +267,12 @@ namespace Dalamud
private void OnOpenInstallerCommand(string command, string arguments) private void OnOpenInstallerCommand(string command, string arguments)
{ {
this.dalamud.DalamudUi.TogglePluginInstaller(); this.dalamud.DalamudUi.TogglePluginInstallerWindow();
} }
private void OnOpenCreditsCommand(string command, string arguments) private void OnOpenCreditsCommand(string command, string arguments)
{ {
this.dalamud.DalamudUi.ToggleCredits(); this.dalamud.DalamudUi.ToggleCreditsWindow();
} }
private void OnSetLanguageCommand(string command, string arguments) private void OnSetLanguageCommand(string command, string arguments)
@ -299,7 +299,7 @@ namespace Dalamud
private void OnOpenSettingsCommand(string command, string arguments) 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;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using CheapLoc; using Dalamud.Interface.Internal.Windows;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Scratchpad;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Internal;
using ImGuiNET; using ImGuiNET;
using Serilog;
using Serilog.Events; using Serilog.Events;
namespace Dalamud.Interface namespace Dalamud.Interface.Internal
{ {
/// <summary> /// <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> /// </summary>
internal class DalamudInterface : IDisposable internal class DalamudInterface : IDisposable
{ {
private static readonly ModuleLog Log = new("DUI");
private readonly Dalamud dalamud; private readonly Dalamud dalamud;
private readonly WindowSystem windowSystem;
private readonly DalamudLogWindow logWindow; private readonly ChangelogWindow changelogWindow;
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 ColorDemoWindow colorDemoWindow; 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 GamepadModeNotifierWindow gamepadModeNotifierWindow;
private readonly LogWindow logWindow;
private readonly WindowSystem windowSystem = new("DalamudCore"); private readonly PluginStatWindow pluginStatWindow;
private readonly PluginInstallerWindow pluginWindow;
private readonly ScratchpadWindow scratchpadWindow;
private readonly SettingsWindow settingsWindow;
private ulong frameCount = 0; private ulong frameCount = 0;
private bool isImguiDrawDemoWindow = false;
#if DEBUG #if DEBUG
private bool isImguiDrawDevMenu = true; private bool isImGuiDrawDevMenu = true;
#else #else
private bool isImguiDrawDevMenu = false; private bool isImGuiDrawDevMenu = false;
#endif #endif
private bool isImGuiDrawDemoWindow = false;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DalamudInterface"/> class. /// Initializes a new instance of the <see cref="DalamudInterface"/> class.
/// </summary> /// </summary>
/// <param name="dalamud">The Dalamud instance to register to.</param> /// <param name="dalamud">The Dalamud instance.</param>
public DalamudInterface(Dalamud dalamud) public DalamudInterface(Dalamud dalamud)
{ {
this.dalamud = dalamud; this.dalamud = dalamud;
this.windowSystem = new WindowSystem("DalamudCore");
this.logWindow = new DalamudLogWindow(this.dalamud.CommandManager, this.dalamud.Configuration) this.changelogWindow = new ChangelogWindow(dalamud) { IsOpen = false };
{ this.colorDemoWindow = new ColorDemoWindow() { IsOpen = false };
IsOpen = false, this.componentDemoWindow = new ComponentDemoWindow() { IsOpen = false };
}; this.creditsWindow = new CreditsWindow(dalamud) { IsOpen = false };
this.windowSystem.AddWindow(this.logWindow); 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) this.windowSystem.AddWindow(this.changelogWindow);
{ this.windowSystem.AddWindow(this.colorDemoWindow);
IsOpen = false, this.windowSystem.AddWindow(this.componentDemoWindow);
};
this.windowSystem.AddWindow(this.dataWindow);
this.creditsWindow = new DalamudCreditsWindow(this.dalamud)
{
IsOpen = false,
};
this.windowSystem.AddWindow(this.creditsWindow); this.windowSystem.AddWindow(this.creditsWindow);
this.windowSystem.AddWindow(this.dataWindow);
this.settingsWindow = new DalamudSettingsWindow(this.dalamud) this.windowSystem.AddWindow(this.gamepadModeNotifierWindow);
{ this.windowSystem.AddWindow(this.logWindow);
IsOpen = false, this.windowSystem.AddWindow(this.pluginStatWindow);
}; this.windowSystem.AddWindow(this.pluginWindow);
this.windowSystem.AddWindow(this.scratchpadWindow);
this.windowSystem.AddWindow(this.settingsWindow); this.windowSystem.AddWindow(this.settingsWindow);
this.pluginWindow = new PluginInstallerWindow(this.dalamud, this.dalamud.StartInfo.GameVersion) this.dalamud.InterfaceManager.OnDraw += this.OnDraw;
{
IsOpen = false,
};
this.windowSystem.AddWindow(this.pluginWindow);
this.pluginStatWindow = new DalamudPluginStatWindow(this.dalamud.PluginManager) Log.Information("Windows added");
{
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();
} }
/// <summary> /// <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> /// </summary>
public bool IsDevMenu public bool IsDevMenuOpen
{ {
get => this.isImguiDrawDevMenu; get => this.isImGuiDrawDevMenu;
set => this.isImguiDrawDevMenu = value; set => this.isImGuiDrawDevMenu = value;
} }
/// <summary> /// <summary>
/// Draw the Dalamud core interface via ImGui. /// Gets a value indicating whether the current Dalamud version warrants displaying the changelog.
/// </summary> /// </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++; this.frameCount++;
if (!this.IsDevMenu && !this.dalamud.ClientState.Condition.Any()) try
{ {
ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0, 0, 0, 0)); this.DrawHiddenDevMenuOpener();
ImGui.PushStyleColor(ImGuiCol.ButtonActive, new Vector4(0, 0, 0, 0)); this.DrawDevMenu();
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(0, 0, 0, 0));
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.Text, new Vector4(0, 0, 0, 1));
ImGui.PushStyleColor(ImGuiCol.TextSelectedBg, 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)); 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.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))) if (ImGui.Button("###devMenuOpener", new Vector2(40, 25)))
this.IsDevMenu = true; this.isImGuiDrawDevMenu = true;
ImGui.End(); ImGui.End();
} }
ImGui.PopStyleColor(8); ImGui.PopStyleColor(8);
} }
}
if (this.IsDevMenu) private void DrawDevMenu()
{
if (this.isImGuiDrawDevMenu)
{ {
if (ImGui.BeginMainMenuBar()) if (ImGui.BeginMainMenuBar())
{ {
if (ImGui.BeginMenu("Dalamud")) 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(); ImGui.Separator();
if (ImGui.MenuItem("Open Log window")) if (ImGui.MenuItem("Open Log window"))
{ {
this.OpenLog(); this.OpenLogWindow();
} }
if (ImGui.BeginMenu("Set log level...")) if (ImGui.BeginMenu("Set log level..."))
@ -214,12 +353,12 @@ namespace Dalamud.Interface
if (ImGui.MenuItem("Open Data window")) if (ImGui.MenuItem("Open Data window"))
{ {
this.OpenData(); this.OpenDataWindow();
} }
if (ImGui.MenuItem("Open Credits window")) if (ImGui.MenuItem("Open Credits window"))
{ {
this.OpenCredits(); this.OpenCreditsWindow();
} }
if (ImGui.MenuItem("Open Settings window")) if (ImGui.MenuItem("Open Settings window"))
@ -229,20 +368,20 @@ namespace Dalamud.Interface
if (ImGui.MenuItem("Open Changelog window")) if (ImGui.MenuItem("Open Changelog window"))
{ {
this.OpenChangelog(); this.OpenChangelogWindow();
} }
if (ImGui.MenuItem("Open Components Demo")) if (ImGui.MenuItem("Open Components Demo"))
{ {
this.OpenComponentDemo(); this.OpenComponentDemoWindow();
} }
if (ImGui.MenuItem("Open Colors Demo")) 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(); ImGui.Separator();
@ -264,12 +403,12 @@ namespace Dalamud.Interface
ImGui.Separator(); ImGui.Separator();
if (ImGui.MenuItem("Enable Dalamud testing", string.Empty, this.dalamud.Configuration.DoDalamudTest)) 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(); this.dalamud.Configuration.Save();
} }
ImGui.MenuItem(Util.AssemblyVersion, false); ImGui.MenuItem(Util.AssemblyVersion, false);
ImGui.MenuItem(this.dalamud.StartInfo.GameVersion, false); ImGui.MenuItem(this.dalamud.StartInfo.GameVersion.ToString(), false);
ImGui.EndMenu(); ImGui.EndMenu();
} }
@ -300,10 +439,10 @@ namespace Dalamud.Interface
if (ImGui.MenuItem("Print plugin info")) 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? // TODO: some more here, state maybe?
Log.Information($"{plugin.Plugin.Name}"); PluginLog.Information($"{plugin.Name}");
} }
} }
@ -311,18 +450,23 @@ namespace Dalamud.Interface
{ {
try try
{ {
this.dalamud.PluginManager.ReloadPlugins(); this.dalamud.PluginManager.ReloadAllPlugins();
} }
catch (Exception ex) catch (Exception ex)
{ {
this.dalamud.Framework.Gui.Chat.PrintError("Reload failed."); 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.Separator();
ImGui.MenuItem("API Level:" + PluginManager.DalamudApiLevel, false); 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(); ImGui.EndMenu();
} }
@ -384,197 +528,6 @@ namespace Dalamud.Interface
ImGui.EndMainMenuBar(); 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.ClientState;
using Dalamud.Game.Internal.DXGI; using Dalamud.Game.Internal.DXGI;
using Dalamud.Hooking; using Dalamud.Hooking;
using EasyHook;
using ImGuiNET; using ImGuiNET;
using ImGuiScene; using ImGuiScene;
using Serilog; 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. * - 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> /// <summary>
/// This class manages interaction with the ImGui interface. /// This class manages interaction with the ImGui interface.
/// </summary> /// </summary>
internal class InterfaceManager : IDisposable 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 Dalamud dalamud;
private readonly string rtssPath;
private readonly Hook<PresentDelegate> presentHook; private readonly Hook<PresentDelegate> presentHook;
private readonly Hook<ResizeBuffersDelegate> resizeBuffersHook; private readonly Hook<ResizeBuffersDelegate> resizeBuffersHook;
private readonly Hook<SetCursorDelegate> setCursorHook; private readonly Hook<SetCursorDelegate> setCursorHook;
private ManualResetEvent fontBuildSignal; private readonly ManualResetEvent fontBuildSignal;
private ISwapChainAddressResolver address; private readonly ISwapChainAddressResolver address;
private RawDX11Scene scene; private RawDX11Scene scene;
private string rtssPath;
// can't access imgui IO before first present call // can't access imgui IO before first present call
private bool lastWantCapture = false; private bool lastWantCapture = false;
private bool isRebuildingFonts = false; private bool isRebuildingFonts = false;
@ -95,12 +83,12 @@ namespace Dalamud.Interface
try try
{ {
var rtss = NativeFunctions.GetModuleHandle("RTSSHooks64.dll"); var rtss = NativeFunctions.GetModuleHandleW("RTSSHooks64.dll");
if (rtss != IntPtr.Zero) if (rtss != IntPtr.Zero)
{ {
var fileName = new StringBuilder(255); var fileName = new StringBuilder(255);
NativeFunctions.GetModuleFileName(rtss, fileName, fileName.Capacity); _ = NativeFunctions.GetModuleFileNameW(rtss, fileName, fileName.Capacity);
this.rtssPath = fileName.ToString(); this.rtssPath = fileName.ToString();
Log.Verbose("RTSS at {0}", this.rtssPath); Log.Verbose("RTSS at {0}", this.rtssPath);
@ -113,17 +101,16 @@ namespace Dalamud.Interface
Log.Error(e, "RTSS Free failed"); 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("===== S W A P C H A I N =====");
Log.Verbose("SetCursor address {SetCursor}", setCursorAddr); Log.Verbose($"SetCursor address 0x{setCursorAddr.ToInt64():X}");
Log.Verbose("Present address {Present}", this.address.Present); Log.Verbose($"Present address 0x{this.address.Present.ToInt64():X}");
Log.Verbose("ResizeBuffers address {ResizeBuffers}", this.address.ResizeBuffers); Log.Verbose($"ResizeBuffers address 0x{this.address.ResizeBuffers.ToInt64():X}");
this.setCursorHook = new Hook<SetCursorDelegate>(setCursorAddr, this.SetCursorDetour); this.setCursorHook = new Hook<SetCursorDelegate>(setCursorAddr, this.SetCursorDetour);
this.presentHook = new Hook<PresentDelegate>(this.address.Present, this.PresentDetour); this.presentHook = new Hook<PresentDelegate>(this.address.Present, this.PresentDetour);
this.resizeBuffersHook = new Hook<ResizeBuffersDelegate>(this.address.ResizeBuffers, this.ResizeBuffersDetour); this.resizeBuffersHook = new Hook<ResizeBuffersDelegate>(this.address.ResizeBuffers, this.ResizeBuffersDetour);
} }
@ -153,6 +140,16 @@ namespace Dalamud.Interface
/// </summary> /// </summary>
public static ImFontPtr IconFont { get; private set; } 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> /// <summary>
/// Gets the D3D11 device instance. /// Gets the D3D11 device instance.
/// </summary> /// </summary>
@ -195,11 +192,11 @@ namespace Dalamud.Interface
{ {
if (!string.IsNullOrEmpty(this.rtssPath)) 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"); Marshal.GetDelegateForFunctionPointer<InstallRTSSHook>(installAddr).Invoke();
var installDele = Marshal.GetDelegateForFunctionPointer<InstallRTSSHook>(installAddr);
installDele.Invoke();
} }
} }
catch (Exception ex) catch (Exception ex)
@ -567,7 +564,7 @@ namespace Dalamud.Interface
{ {
ImGui.GetIO().ConfigFlags ^= ImGuiConfigFlags.NavEnableGamepad; ImGui.GetIO().ConfigFlags ^= ImGuiConfigFlags.NavEnableGamepad;
this.dalamud.ClientState.GamepadState.NavEnableGamepad ^= true; this.dalamud.ClientState.GamepadState.NavEnableGamepad ^= true;
this.dalamud.DalamudUi.ToggleGamePadNotifierWindow(); this.dalamud.DalamudUi.ToggleGamepadModeNotifierWindow();
} }
if (gamepadEnabled if (gamepadEnabled
@ -592,7 +589,7 @@ namespace Dalamud.Interface
if (this.dalamud.ClientState.GamepadState.Pressed(GamepadButtons.R3) > 0) 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 Microsoft.CodeAnalysis.Scripting;
using Serilog; using Serilog;
namespace Dalamud.Interface.Scratchpad namespace Dalamud.Interface.Internal.Scratchpad
{ {
/// <summary> /// <summary>
/// This class manages the execution of <see cref="ScratchpadDocument"/> classes. /// 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 script = CSharpScript.Create(code, options);
var pi = new DalamudPluginInterface(this.dalamud, "Scratch-" + doc.Id, null, PluginLoadReason.Installer); var pi = new DalamudPluginInterface(this.dalamud, "Scratch-" + doc.Id, null);
var plugin = script.ContinueWith<IDalamudPlugin>("return new ScratchPlugin() as IDalamudPlugin;").RunAsync().GetAwaiter().GetResult() var plugin = script.ContinueWith<IDalamudPlugin>("return new ScratchPlugin() as IDalamudPlugin;")
.ReturnValue; .RunAsync().GetAwaiter().GetResult().ReturnValue;
plugin.Initialize(pi); plugin.Initialize(pi);

View file

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

View file

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