mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-03 06:13:40 +01:00
Merge branch 'master' of https://github.com/goatcorp/Dalamud into goatcorp-master
This commit is contained in:
commit
f215fa77ee
249 changed files with 13902 additions and 5365 deletions
|
|
@ -24,6 +24,7 @@ csharp_preferred_modifier_order = public, private, protected, internal, new, abs
|
|||
csharp_style_var_elsewhere = true:suggestion
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
dotnet_code_quality_unused_parameters = non_public
|
||||
dotnet_naming_rule.event_rule.severity = warning
|
||||
dotnet_naming_rule.event_rule.style = on_upper_camel_case_style
|
||||
dotnet_naming_rule.event_rule.symbols = event_symbols
|
||||
|
|
@ -56,11 +57,13 @@ dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static
|
|||
dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private
|
||||
dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static,readonly
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:suggestion
|
||||
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:suggestion
|
||||
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators =always_for_clarity:suggestion
|
||||
dotnet_style_parentheses_in_other_binary_operators =always_for_clarity:suggestion
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
|
||||
dotnet_style_parentheses_in_other_operators=always_for_clarity:silent
|
||||
dotnet_style_object_initializer = false
|
||||
|
||||
# ReSharper properties
|
||||
resharper_align_linq_query = true
|
||||
|
|
@ -105,8 +108,10 @@ resharper_redundant_base_qualifier_highlighting = none
|
|||
resharper_suggest_var_or_type_built_in_types_highlighting = hint
|
||||
resharper_suggest_var_or_type_elsewhere_highlighting = hint
|
||||
resharper_suggest_var_or_type_simple_types_highlighting = hint
|
||||
csharp_style_deconstructed_variable_declaration=true:silent
|
||||
|
||||
[*.{appxmanifest,asax,ascx,aspx,axaml,axml,build,c,c++,cc,cginc,compute,config,cp,cpp,cs,cshtml,csproj,css,cu,cuh,cxx,dbml,discomap,dtd,h,hh,hlsl,hlsli,hlslinc,hpp,htm,html,hxx,inc,inl,ino,ipp,js,json,jsproj,jsx,lsproj,master,mpp,mq4,mq5,mqh,njsproj,nuspec,paml,proj,props,proto,razor,resjson,resw,resx,skin,StyleCop,targets,tasks,tpp,ts,tsx,usf,ush,vb,vbproj,xaml,xamlx,xml,xoml,xsd}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
dotnet_style_parentheses_in_other_operators=always_for_clarity:silent
|
||||
|
|
|
|||
2
.github/workflows/delete-artifacts.yml
vendored
2
.github/workflows/delete-artifacts.yml
vendored
|
|
@ -12,7 +12,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Remove old artifacts
|
||||
uses: c-hive/gha-remove-artifacts@24dc23384a1fa6a058b79c73727ae0cb2200ca4c
|
||||
uses: c-hive/gha-remove-artifacts@v1.2.0
|
||||
with:
|
||||
age: '1 month'
|
||||
skip-tags: true
|
||||
|
|
|
|||
28
.github/workflows/main.yml
vendored
28
.github/workflows/main.yml
vendored
|
|
@ -7,15 +7,12 @@ jobs:
|
|||
name: Build on Windows
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout Dalamud
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Setup Nuget
|
||||
uses: nuget/setup-nuget@v1
|
||||
with:
|
||||
nuget-version: latest
|
||||
- name: Restore Nuget Packages
|
||||
run: nuget restore Dalamud.sln
|
||||
- name: Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v1.0.2
|
||||
- name: Define VERSION
|
||||
run: |
|
||||
$env:COMMIT = $env:GITHUB_SHA.Substring(0, 7)
|
||||
|
|
@ -25,18 +22,17 @@ jobs:
|
|||
($env:REPO_NAME) >> VERSION
|
||||
($env:BRANCH) >> VERSION
|
||||
($env:COMMIT) >> VERSION
|
||||
- name: Build DotNet4
|
||||
run: |
|
||||
cd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\"
|
||||
.\MSBuild.exe $Env:GITHUB_WORKSPACE\Dalamud.sln /t:Build /p:Configuration=Release /p:DefineConstants=XL_NOAUTOUPDATE
|
||||
- name: Run xUnit Tests
|
||||
run: |
|
||||
${{github.workspace}}\packages\xunit.runner.console.2.4.1\tools\net472\xunit.console.exe ${{github.workspace}}\Dalamud.Test\bin\Release\Dalamud.Test.dll
|
||||
- name: Build Dalamud
|
||||
run: .\build.ps1 compile
|
||||
- name: Test Dalamud
|
||||
run: .\build.ps1 test
|
||||
- name: Create hashlist
|
||||
run: .\CreateHashList.ps1 .\bin\Release
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: dalamud-artifact
|
||||
path: bin\
|
||||
path: bin\Release
|
||||
|
||||
deploy_stg:
|
||||
name: Deploy dalamud-distrib staging
|
||||
|
|
@ -54,7 +50,7 @@ jobs:
|
|||
path: .\scratch
|
||||
|
||||
- name: Generate dalamud-distrib version file
|
||||
shell: powershell
|
||||
shell: pwsh
|
||||
run: |
|
||||
Compress-Archive .\scratch\* .\canary.zip # Recreate the release zip
|
||||
|
||||
|
|
|
|||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -4,3 +4,6 @@
|
|||
[submodule "lib/FFXIVClientStructs"]
|
||||
path = lib/FFXIVClientStructs
|
||||
url = https://github.com/goatcorp/FFXIVClientStructs.git
|
||||
[submodule "lib/SharpDX.Desktop"]
|
||||
path = lib/SharpDX.Desktop
|
||||
url = https://github.com/goatcorp/SharpDX.Desktop.git
|
||||
|
|
|
|||
112
.nuke/build.schema.json
Normal file
112
.nuke/build.schema.json
Normal 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
4
.nuke/parameters.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "./build.schema.json",
|
||||
"Solution": "Dalamud.sln"
|
||||
}
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
$hashes = @{}
|
||||
$hashes = [ordered]@{}
|
||||
|
||||
Get-ChildItem $args[0] -Exclude dalamud.txt,*.zip,*.pdb,*.ipdb | Foreach-Object {
|
||||
$hashes.Add($_.Name, (Get-FileHash $_.FullName -Algorithm MD5).Hash)
|
||||
Set-Location $args[0]
|
||||
|
||||
Get-ChildItem -File -Recurse -Exclude dalamud.txt,*.zip,*.pdb,*.ipdb | Foreach-Object {
|
||||
$key = ($_.FullName | Resolve-Path -Relative).TrimStart(".\\")
|
||||
$val = (Get-FileHash $_.FullName -Algorithm MD5).Hash
|
||||
$hashes.Add($key, $val)
|
||||
}
|
||||
|
||||
ConvertTo-Json $hashes | Out-File -FilePath (Join-Path $args[0] "hashes.json")
|
||||
$hashes | ConvertTo-Json | Out-File -FilePath "hashes.json"
|
||||
105
Dalamud.Boot/Dalamud.Boot.vcxproj
Normal file
105
Dalamud.Boot/Dalamud.Boot.vcxproj
Normal 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>
|
||||
62
Dalamud.Boot/Dalamud.Boot.vcxproj.filters
Normal file
62
Dalamud.Boot/Dalamud.Boot.vcxproj.filters
Normal 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
66
Dalamud.Boot/dllmain.cpp
Normal 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(NDEBUG)
|
||||
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(NDEBUG)
|
||||
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;
|
||||
}
|
||||
61
Dalamud.CorePlugin/Dalamud.CorePlugin.csproj
Normal file
61
Dalamud.CorePlugin/Dalamud.CorePlugin.csproj
Normal 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>
|
||||
17
Dalamud.CorePlugin/GlobalSuppressions.cs
Normal file
17
Dalamud.CorePlugin/GlobalSuppressions.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// 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")]
|
||||
[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", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
|
||||
96
Dalamud.CorePlugin/PluginImpl.cs
Normal file
96
Dalamud.CorePlugin/PluginImpl.cs
Normal 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(new PluginWindow(Dalamud.Instance));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
47
Dalamud.CorePlugin/PluginWindow.cs
Normal file
47
Dalamud.CorePlugin/PluginWindow.cs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
using Dalamud.Interface.Windowing;
|
||||
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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
113
Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj
Normal file
113
Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj
Normal 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>
|
||||
75
Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj.filters
Normal file
75
Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj.filters
Normal 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 |
49
Dalamud.Injector.Boot/main.cpp
Normal file
49
Dalamud.Injector.Boot/main.cpp
Normal 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(NDEBUG)
|
||||
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(NDEBUG)
|
||||
FreeConsole();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
1
Dalamud.Injector.Boot/resources.rc
Normal file
1
Dalamud.Injector.Boot/resources.rc
Normal file
|
|
@ -0,0 +1 @@
|
|||
MAINICON ICON "dalamud.ico"
|
||||
|
|
@ -1,60 +1,89 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup Label="Target">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Build">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<OutputPath>$(SolutionDir)bin</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>Portable</DebugType>
|
||||
<NoWarn>IDE1006;CS1701;CS1702</NoWarn>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<DocumentationFile>$(SolutionDir)\bin\Dalamud.Injector.xml</DocumentationFile>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Platforms>x64;AnyCPU</Platforms>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Feature">
|
||||
<InjectorVersion>5.2.4.6</InjectorVersion>
|
||||
<Description>XIV Launcher addon injector</Description>
|
||||
<AssemblyVersion>$(InjectorVersion)</AssemblyVersion>
|
||||
<FileVersion>$(InjectorVersion)</FileVersion>
|
||||
<Version>$(InjectorVersion)</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Output">
|
||||
<OutputType>Library</OutputType>
|
||||
<OutputPath>..\bin\$(Configuration)\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Documentation">
|
||||
<DocumentationFile></DocumentationFile>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Build">
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<Deterministic>true</Deterministic>
|
||||
<Nullable>annotations</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<AssemblyVersion>5.2.4.6</AssemblyVersion>
|
||||
<FileVersion>5.2.4.6</FileVersion>
|
||||
<Description>XIVLauncher addon injection</Description>
|
||||
<Version>5.2.4.6</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Configuration">
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
<AppOutputBase>$(MSBuildProjectDirectory)\</AppOutputBase>
|
||||
<PathMap>$(AppOutputBase)=C:\goatsoft\companysecrets\injector\</PathMap>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
<PackageIcon></PackageIcon>
|
||||
<PackageIconUrl />
|
||||
<ApplicationIcon>dalamud.ico</ApplicationIcon>
|
||||
|
||||
<PropertyGroup Label="Warnings">
|
||||
<NoWarn>IDE1006;CS1591;CS1701;CS1702</NoWarn>
|
||||
<!-- IDE1006 - Naming violation -->
|
||||
<!-- CS1591 - Missing XML comment for publicly visible type or member -->
|
||||
<!-- CS1701 - Runtime policy may be needed -->
|
||||
<!-- CS1702 - Runtime policy may be needed -->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="stylecop.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="stylecop.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EasyHook" Version="2.7.6270" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="Iced" Version="1.12.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="PeNet" Version="2.6.3" />
|
||||
<PackageReference Include="Reloaded.Memory" Version="4.1.1" />
|
||||
<PackageReference Include="Reloaded.Memory.Buffers" Version="1.3.5" />
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.333">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DalamudDebugStub\DalamudDebugStub.vcxproj" />
|
||||
<ProjectReference Include="..\Dalamud\Dalamud.csproj" />
|
||||
<AdditionalFiles Include="..\stylecop.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<!-- This prevents us from having to include Dalamud itself as a dependency -->
|
||||
<!-- If the files move just update the paths here -->
|
||||
<Compile Include="..\Dalamud\ClientLanguage.cs" />
|
||||
<Compile Include="..\Dalamud\DalamudStartInfo.cs" />
|
||||
<Compile Include="..\Dalamud\Game\GameVersion.cs" />
|
||||
<Compile Include="..\Dalamud\Game\GameVersionConverter.cs" />
|
||||
<Compile Include="..\Dalamud\Interface\Internal\SerilogEventSink.cs" />
|
||||
</ItemGroup>
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)'=='Release'">
|
||||
<Exec Command="powershell -ExecutionPolicy Unrestricted $(SolutionDir)CreateHashList.ps1 $(OutputPath)" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
|
|||
275
Dalamud.Injector/EntryPoint.cs
Normal file
275
Dalamud.Injector/EntryPoint.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,14 +6,8 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
// General
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = "Preventing long lines", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "I like regions", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "I like regions in elements too", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "This is annoying", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1127:Generic type constraints should be on their own line", Justification = "I like this better", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "We don't do those yet")]
|
||||
|
||||
// Program.cs
|
||||
[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used during #if DEBUG", Scope = "member", Target = "~M:Dalamud.Injector.Program.NativeInject(System.Diagnostics.Process)")]
|
||||
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "I'll make what I want static", Scope = "namespaceanddescendants", Target = "~N:Dalamud")]
|
||||
|
|
|
|||
303
Dalamud.Injector/Injector.cs
Normal file
303
Dalamud.Injector/Injector.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,234 @@
|
|||
using System;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace Dalamud.Injector
|
||||
{
|
||||
/// <summary>
|
||||
/// Native functions.
|
||||
/// Native user32 functions.
|
||||
/// </summary>
|
||||
internal static class NativeFunctions
|
||||
internal static partial class NativeFunctions
|
||||
{
|
||||
/// <summary>
|
||||
/// MB_* from winuser.
|
||||
/// </summary>
|
||||
public enum MessageBoxType : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// The default value for any of the various subtypes.
|
||||
/// </summary>
|
||||
DefaultValue = 0x0,
|
||||
|
||||
// To indicate the buttons displayed in the message box, specify one of the following values.
|
||||
|
||||
/// <summary>
|
||||
/// The message box contains three push buttons: Abort, Retry, and Ignore.
|
||||
/// </summary>
|
||||
AbortRetryIgnore = 0x2,
|
||||
|
||||
/// <summary>
|
||||
/// The message box contains three push buttons: Cancel, Try Again, Continue. Use this message box type instead
|
||||
/// of MB_ABORTRETRYIGNORE.
|
||||
/// </summary>
|
||||
CancelTryContinue = 0x6,
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Help button to the message box. When the user clicks the Help button or presses F1, the system sends
|
||||
/// a WM_HELP message to the owner.
|
||||
/// </summary>
|
||||
Help = 0x4000,
|
||||
|
||||
/// <summary>
|
||||
/// The message box contains one push button: OK. This is the default.
|
||||
/// </summary>
|
||||
Ok = DefaultValue,
|
||||
|
||||
/// <summary>
|
||||
/// The message box contains two push buttons: OK and Cancel.
|
||||
/// </summary>
|
||||
OkCancel = 0x1,
|
||||
|
||||
/// <summary>
|
||||
/// The message box contains two push buttons: Retry and Cancel.
|
||||
/// </summary>
|
||||
RetryCancel = 0x5,
|
||||
|
||||
/// <summary>
|
||||
/// The message box contains two push buttons: Yes and No.
|
||||
/// </summary>
|
||||
YesNo = 0x4,
|
||||
|
||||
/// <summary>
|
||||
/// The message box contains three push buttons: Yes, No, and Cancel.
|
||||
/// </summary>
|
||||
YesNoCancel = 0x3,
|
||||
|
||||
// To display an icon in the message box, specify one of the following values.
|
||||
|
||||
/// <summary>
|
||||
/// An exclamation-point icon appears in the message box.
|
||||
/// </summary>
|
||||
IconExclamation = 0x30,
|
||||
|
||||
/// <summary>
|
||||
/// An exclamation-point icon appears in the message box.
|
||||
/// </summary>
|
||||
IconWarning = IconExclamation,
|
||||
|
||||
/// <summary>
|
||||
/// An icon consisting of a lowercase letter i in a circle appears in the message box.
|
||||
/// </summary>
|
||||
IconInformation = 0x40,
|
||||
|
||||
/// <summary>
|
||||
/// An icon consisting of a lowercase letter i in a circle appears in the message box.
|
||||
/// </summary>
|
||||
IconAsterisk = IconInformation,
|
||||
|
||||
/// <summary>
|
||||
/// A question-mark icon appears in the message box.
|
||||
/// The question-mark message icon is no longer recommended because it does not clearly represent a specific type
|
||||
/// of message and because the phrasing of a message as a question could apply to any message type. In addition,
|
||||
/// users can confuse the message symbol question mark with Help information. Therefore, do not use this question
|
||||
/// mark message symbol in your message boxes. The system continues to support its inclusion only for backward
|
||||
/// compatibility.
|
||||
/// </summary>
|
||||
IconQuestion = 0x20,
|
||||
|
||||
/// <summary>
|
||||
/// A stop-sign icon appears in the message box.
|
||||
/// </summary>
|
||||
IconStop = 0x10,
|
||||
|
||||
/// <summary>
|
||||
/// A stop-sign icon appears in the message box.
|
||||
/// </summary>
|
||||
IconError = IconStop,
|
||||
|
||||
/// <summary>
|
||||
/// A stop-sign icon appears in the message box.
|
||||
/// </summary>
|
||||
IconHand = IconStop,
|
||||
|
||||
// To indicate the default button, specify one of the following values.
|
||||
|
||||
/// <summary>
|
||||
/// The first button is the default button.
|
||||
/// MB_DEFBUTTON1 is the default unless MB_DEFBUTTON2, MB_DEFBUTTON3, or MB_DEFBUTTON4 is specified.
|
||||
/// </summary>
|
||||
DefButton1 = DefaultValue,
|
||||
|
||||
/// <summary>
|
||||
/// The second button is the default button.
|
||||
/// </summary>
|
||||
DefButton2 = 0x100,
|
||||
|
||||
/// <summary>
|
||||
/// The third button is the default button.
|
||||
/// </summary>
|
||||
DefButton3 = 0x200,
|
||||
|
||||
/// <summary>
|
||||
/// The fourth button is the default button.
|
||||
/// </summary>
|
||||
DefButton4 = 0x300,
|
||||
|
||||
// To indicate the modality of the dialog box, specify one of the following values.
|
||||
|
||||
/// <summary>
|
||||
/// The user must respond to the message box before continuing work in the window identified by the hWnd parameter.
|
||||
/// However, the user can move to the windows of other threads and work in those windows. Depending on the hierarchy
|
||||
/// of windows in the application, the user may be able to move to other windows within the thread. All child windows
|
||||
/// of the parent of the message box are automatically disabled, but pop-up windows are not. MB_APPLMODAL is the
|
||||
/// default if neither MB_SYSTEMMODAL nor MB_TASKMODAL is specified.
|
||||
/// </summary>
|
||||
ApplModal = DefaultValue,
|
||||
|
||||
/// <summary>
|
||||
/// Same as MB_APPLMODAL except that the message box has the WS_EX_TOPMOST style.
|
||||
/// Use system-modal message boxes to notify the user of serious, potentially damaging errors that require immediate
|
||||
/// attention (for example, running out of memory). This flag has no effect on the user's ability to interact with
|
||||
/// windows other than those associated with hWnd.
|
||||
/// </summary>
|
||||
SystemModal = 0x1000,
|
||||
|
||||
/// <summary>
|
||||
/// Same as MB_APPLMODAL except that all the top-level windows belonging to the current thread are disabled if the
|
||||
/// hWnd parameter is NULL. Use this flag when the calling application or library does not have a window handle
|
||||
/// available but still needs to prevent input to other windows in the calling thread without suspending other threads.
|
||||
/// </summary>
|
||||
TaskModal = 0x2000,
|
||||
|
||||
// To specify other options, use one or more of the following values.
|
||||
|
||||
/// <summary>
|
||||
/// Same as desktop of the interactive window station. For more information, see Window Stations. If the current
|
||||
/// input desktop is not the default desktop, MessageBox does not return until the user switches to the default
|
||||
/// desktop.
|
||||
/// </summary>
|
||||
DefaultDesktopOnly = 0x20000,
|
||||
|
||||
/// <summary>
|
||||
/// The text is right-justified.
|
||||
/// </summary>
|
||||
Right = 0x80000,
|
||||
|
||||
/// <summary>
|
||||
/// Displays message and caption text using right-to-left reading order on Hebrew and Arabic systems.
|
||||
/// </summary>
|
||||
RtlReading = 0x100000,
|
||||
|
||||
/// <summary>
|
||||
/// The message box becomes the foreground window. Internally, the system calls the SetForegroundWindow function
|
||||
/// for the message box.
|
||||
/// </summary>
|
||||
SetForeground = 0x10000,
|
||||
|
||||
/// <summary>
|
||||
/// The message box is created with the WS_EX_TOPMOST window style.
|
||||
/// </summary>
|
||||
Topmost = 0x40000,
|
||||
|
||||
/// <summary>
|
||||
/// The caller is a service notifying the user of an event. The function displays a message box on the current active
|
||||
/// desktop, even if there is no user logged on to the computer.
|
||||
/// </summary>
|
||||
ServiceNotification = 0x200000,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays a modal dialog box that contains a system icon, a set of buttons, and a brief application-specific message,
|
||||
/// such as status or error information. The message box returns an integer value that indicates which button the user
|
||||
/// clicked.
|
||||
/// </summary>
|
||||
/// <param name="hWnd">
|
||||
/// A handle to the owner window of the message box to be created. If this parameter is NULL, the message box has no
|
||||
/// owner window.
|
||||
/// </param>
|
||||
/// <param name="text">
|
||||
/// The message to be displayed. If the string consists of more than one line, you can separate the lines using a carriage
|
||||
/// return and/or linefeed character between each line.
|
||||
/// </param>
|
||||
/// <param name="caption">
|
||||
/// The dialog box title. If this parameter is NULL, the default title is Error.</param>
|
||||
/// <param name="type">
|
||||
/// The contents and behavior of the dialog box. This parameter can be a combination of flags from the following groups
|
||||
/// of flags.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If a message box has a Cancel button, the function returns the IDCANCEL value if either the ESC key is pressed or
|
||||
/// the Cancel button is selected. If the message box has no Cancel button, pressing ESC will no effect - unless an
|
||||
/// MB_OK button is present. If an MB_OK button is displayed and the user presses ESC, the return value will be IDOK.
|
||||
/// If the function fails, the return value is zero.To get extended error information, call GetLastError. If the function
|
||||
/// succeeds, the return value is one of the ID* enum values.
|
||||
/// </returns>
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
public static extern int MessageBoxW(IntPtr hWnd, string text, string caption, MessageBoxType type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Native kernel32 functions.
|
||||
/// </summary>
|
||||
internal static partial class NativeFunctions
|
||||
{
|
||||
/// <summary>
|
||||
/// MEM_* from memoryapi.
|
||||
|
|
@ -20,14 +240,14 @@ namespace Dalamud.Injector
|
|||
/// To coalesce two adjacent placeholders, specify MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS. When you coalesce
|
||||
/// placeholders, lpAddress and dwSize must exactly match those of the placeholder.
|
||||
/// </summary>
|
||||
CoalescePlaceholders = 0x00000001,
|
||||
CoalescePlaceholders = 0x1,
|
||||
|
||||
/// <summary>
|
||||
/// Frees an allocation back to a placeholder (after you've replaced a placeholder with a private allocation using
|
||||
/// VirtualAlloc2 or Virtual2AllocFromApp). To split a placeholder into two placeholders, specify
|
||||
/// MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER.
|
||||
/// </summary>
|
||||
PreservePlaceholder = 0x00000002,
|
||||
PreservePlaceholder = 0x2,
|
||||
|
||||
/// <summary>
|
||||
/// Allocates memory charges (from the overall size of memory and the paging files on disk) for the specified reserved
|
||||
|
|
@ -88,7 +308,7 @@ namespace Dalamud.Injector
|
|||
/// the specified address range is intact. If the function fails, at least some of the data in the address range
|
||||
/// has been replaced with zeroes. This value cannot be used with any other value. If MEM_RESET_UNDO is called on
|
||||
/// an address range which was not MEM_RESET earlier, the behavior is undefined. When you specify MEM_RESET, the
|
||||
/// VirtualAllocEx function ignores the value of flProtect. However, you must still set flProtect to a valid
|
||||
/// VirtualAllocEx function ignores the value of flProtect. However, you must still set flProtect to a valid
|
||||
/// protection value, such as PAGE_NOACCESS.
|
||||
/// </summary>
|
||||
ResetUndo = 0x1000000,
|
||||
|
|
@ -122,6 +342,28 @@ namespace Dalamud.Injector
|
|||
LargePages = 0x20000000,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unprefixed flags from CreateRemoteThread.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum CreateThreadFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// The thread runs immediately after creation.
|
||||
/// </summary>
|
||||
RunImmediately = 0x0,
|
||||
|
||||
/// <summary>
|
||||
/// The thread is created in a suspended state, and does not run until the ResumeThread function is called.
|
||||
/// </summary>
|
||||
CreateSuspended = 0x4,
|
||||
|
||||
/// <summary>
|
||||
/// The dwStackSize parameter specifies the initial reserve size of the stack. If this flag is not specified, dwStackSize specifies the commit size.
|
||||
/// </summary>
|
||||
StackSizeParamIsReservation = 0x10000,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PAGE_* from memoryapi.
|
||||
/// </summary>
|
||||
|
|
@ -198,7 +440,7 @@ namespace Dalamud.Injector
|
|||
/// The default behavior for VirtualProtect protection change to executable is to mark all locations as valid call
|
||||
/// targets for CFG.
|
||||
/// </summary>
|
||||
TargetsNoUpdate = 0x40000000,
|
||||
TargetsNoUpdate = TargetsInvalid,
|
||||
|
||||
/// <summary>
|
||||
/// Pages in the region become guard pages. Any attempt to access a guard page causes the system to raise a
|
||||
|
|
@ -312,23 +554,33 @@ namespace Dalamud.Injector
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes an open object handle.
|
||||
/// WAIT_* from synchapi.
|
||||
/// </summary>
|
||||
/// <param name="hObject">
|
||||
/// A valid handle to an open object.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.To get extended
|
||||
/// error information, call GetLastError. If the application is running under a debugger, the function will throw an
|
||||
/// exception if it receives either a handle value that is not valid or a pseudo-handle value. This can happen if you
|
||||
/// close a handle twice, or if you call CloseHandle on a handle returned by the FindFirstFile function instead of calling
|
||||
/// the FindClose function.
|
||||
/// </returns>
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool CloseHandle(IntPtr hObject);
|
||||
public enum WaitResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The specified object is a mutex object that was not released by the thread that owned the mutex object
|
||||
/// before the owning thread terminated.Ownership of the mutex object is granted to the calling thread and
|
||||
/// the mutex state is set to nonsignaled. If the mutex was protecting persistent state information, you
|
||||
/// should check it for consistency.
|
||||
/// </summary>
|
||||
Abandoned = 0x80,
|
||||
|
||||
/// <summary>
|
||||
/// The state of the specified object is signaled.
|
||||
/// </summary>
|
||||
Object0 = 0x0,
|
||||
|
||||
/// <summary>
|
||||
/// The time-out interval elapsed, and the object's state is nonsignaled.
|
||||
/// </summary>
|
||||
Timeout = 0x102,
|
||||
|
||||
/// <summary>
|
||||
/// The function has failed. To get extended error information, call GetLastError.
|
||||
/// </summary>
|
||||
WAIT_FAILED = 0xFFFFFFF,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a thread that runs in the virtual address space of another process. Use the CreateRemoteThreadEx function
|
||||
|
|
@ -336,23 +588,23 @@ namespace Dalamud.Injector
|
|||
/// </summary>
|
||||
/// <param name="hProcess">
|
||||
/// A handle to the process in which the thread is to be created. The handle must have the PROCESS_CREATE_THREAD,
|
||||
/// PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ access rights, and may fail
|
||||
/// without these rights on certain platforms. For more information, see Process Security and Access Rights.
|
||||
/// PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ access rights, and may fail without
|
||||
/// these rights on certain platforms. For more information, see Process Security and Access Rights.
|
||||
/// </param>
|
||||
/// <param name="lpThreadAttributes">
|
||||
/// A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new thread and determines
|
||||
/// whether child processes can inherit the returned handle. If lpThreadAttributes is NULL, the thread gets a default
|
||||
/// security descriptor and the handle cannot be inherited. The access control lists (ACL) in the default security descriptor
|
||||
/// for a thread come from the primary token of the creator.
|
||||
/// A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new thread and determines whether
|
||||
/// child processes can inherit the returned handle. If lpThreadAttributes is NULL, the thread gets a default security descriptor
|
||||
/// and the handle cannot be inherited. The access control lists (ACL) in the default security descriptor for a thread come from
|
||||
/// the primary token of the creator.
|
||||
/// </param>
|
||||
/// <param name="dwStackSize">
|
||||
/// The initial size of the stack, in bytes. The system rounds this value to the nearest page. If this parameter is
|
||||
/// 0 (zero), the new thread uses the default size for the executable. For more information, see Thread Stack Size.
|
||||
/// The initial size of the stack, in bytes. The system rounds this value to the nearest page. If this parameter is 0 (zero), the
|
||||
/// new thread uses the default size for the executable. For more information, see Thread Stack Size.
|
||||
/// </param>
|
||||
/// <param name="lpStartAddress">
|
||||
/// A pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread and
|
||||
/// represents the starting address of the thread in the remote process. The function must exist in the remote process.
|
||||
/// For more information, see ThreadProc.
|
||||
/// A pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread and represents the
|
||||
/// starting address of the thread in the remote process. The function must exist in the remote process. For more information,
|
||||
/// see ThreadProc.
|
||||
/// </param>
|
||||
/// <param name="lpParameter">
|
||||
/// A pointer to a variable to be passed to the thread function.
|
||||
|
|
@ -361,92 +613,43 @@ namespace Dalamud.Injector
|
|||
/// The flags that control the creation of the thread.
|
||||
/// </param>
|
||||
/// <param name="lpThreadId">
|
||||
/// A pointer to a variable that receives the thread identifier. If this parameter is NULL, the thread identifier is
|
||||
/// not returned.
|
||||
/// A pointer to a variable that receives the thread identifier. If this parameter is NULL, the thread identifier is not returned.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value is a handle to the new thread. If the function fails, the return value
|
||||
/// is NULL.To get extended error information, call GetLastError. Note that CreateRemoteThread may succeed even if
|
||||
/// lpStartAddress points to data, code, or is not accessible. If the start address is invalid when the thread runs,
|
||||
/// an exception occurs, and the thread terminates. Thread termination due to a invalid start address is handled as
|
||||
/// an error exit for the thread's process. This behavior is similar to the asynchronous nature of CreateProcess, where
|
||||
/// the process is created even if it refers to invalid or missing dynamic-link libraries (DLL).
|
||||
/// If the function succeeds, the return value is a handle to the new thread. If the function fails, the return value is
|
||||
/// NULL.To get extended error information, call GetLastError. Note that CreateRemoteThread may succeed even if lpStartAddress
|
||||
/// points to data, code, or is not accessible. If the start address is invalid when the thread runs, an exception occurs, and
|
||||
/// the thread terminates. Thread termination due to a invalid start address is handled as an error exit for the thread's process.
|
||||
/// This behavior is similar to the asynchronous nature of CreateProcess, where the process is created even if it refers to
|
||||
/// invalid or missing dynamic-link libraries (DLL).
|
||||
/// </returns>
|
||||
[DllImport("kernel32.dll")]
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr CreateRemoteThread(
|
||||
IntPtr hProcess,
|
||||
IntPtr lpThreadAttributes,
|
||||
uint dwStackSize,
|
||||
UIntPtr dwStackSize,
|
||||
IntPtr lpStartAddress,
|
||||
IntPtr lpParameter,
|
||||
uint dwCreationFlags,
|
||||
IntPtr lpThreadId);
|
||||
CreateThreadFlags dwCreationFlags,
|
||||
out uint lpThreadId);
|
||||
|
||||
/// <summary>
|
||||
/// See https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlew.
|
||||
/// Retrieves a module handle for the specified module. The module must have been loaded by the calling process. To
|
||||
/// avoid the race conditions described in the Remarks section, use the GetModuleHandleEx function.
|
||||
/// Retrieves the termination status of the specified thread.
|
||||
/// </summary>
|
||||
/// <param name="lpModuleName">
|
||||
/// The name of the loaded module (either a .dll or .exe file). If the file name extension is omitted, the default library
|
||||
/// extension .dll is appended. The file name string can include a trailing point character (.) to indicate that the
|
||||
/// module name has no extension. The string does not have to specify a path. When specifying a path, be sure to use
|
||||
/// backslashes (\), not forward slashes (/). The name is compared (case independently) to the names of modules currently
|
||||
/// mapped into the address space of the calling process. If this parameter is NULL, GetModuleHandle returns a handle
|
||||
/// to the file used to create the calling process (.exe file). The GetModuleHandle function does not retrieve handles
|
||||
/// for modules that were loaded using the LOAD_LIBRARY_AS_DATAFILE flag.For more information, see LoadLibraryEx.
|
||||
/// <param name="hThread">
|
||||
/// A handle to the thread. The handle must have the THREAD_QUERY_INFORMATION or THREAD_QUERY_LIMITED_INFORMATION
|
||||
/// access right.For more information, see Thread Security and Access Rights.
|
||||
/// </param>
|
||||
/// <param name="lpExitCode">
|
||||
/// A pointer to a variable to receive the thread termination status.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value is a handle to the specified module. If the function fails, the return
|
||||
/// value is NULL.To get extended error information, call GetLastError.
|
||||
/// </returns>
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr GetModuleHandle(string lpModuleName);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the address of an exported function or variable from the specified dynamic-link library (DLL).
|
||||
/// </summary>
|
||||
/// <param name="hModule">
|
||||
/// A handle to the DLL module that contains the function or variable. The LoadLibrary, LoadLibraryEx, LoadPackagedLibrary,
|
||||
/// or GetModuleHandle function returns this handle. The GetProcAddress function does not retrieve addresses from modules
|
||||
/// that were loaded using the LOAD_LIBRARY_AS_DATAFILE flag.For more information, see LoadLibraryEx.
|
||||
/// </param>
|
||||
/// <param name="procName">
|
||||
/// The function or variable name, or the function's ordinal value. If this parameter is an ordinal value, it must be
|
||||
/// in the low-order word; the high-order word must be zero.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value is the address of the exported function or variable. If the function
|
||||
/// fails, the return value is NULL.To get extended error information, call GetLastError.
|
||||
/// </returns>
|
||||
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
|
||||
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
|
||||
|
||||
/// <summary>
|
||||
/// See https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess.
|
||||
/// Opens an existing local process object.
|
||||
/// </summary>
|
||||
/// <param name="processAccess">
|
||||
/// The access to the process object. This access right is checked against the security descriptor for the process.
|
||||
/// This parameter can be one or more of the process access rights. If the caller has enabled the SeDebugPrivilege
|
||||
/// privilege, the requested access is granted regardless of the contents of the security descriptor.
|
||||
/// </param>
|
||||
/// <param name="bInheritHandle">
|
||||
/// If this value is TRUE, processes created by this process will inherit the handle. Otherwise, the processes do
|
||||
/// not inherit this handle.
|
||||
/// </param>
|
||||
/// <param name="processId">
|
||||
/// The identifier of the local process to be opened.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value is an open handle to the specified process. If the function fails, the
|
||||
/// return value is NULL.To get extended error information, call GetLastError.
|
||||
/// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get
|
||||
/// extended error information, call GetLastError.
|
||||
/// </returns>
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr OpenProcess(
|
||||
ProcessAccessFlags processAccess,
|
||||
bool bInheritHandle,
|
||||
int processId);
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode);
|
||||
|
||||
/// <summary>
|
||||
/// See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex.
|
||||
|
|
@ -530,6 +733,27 @@ namespace Dalamud.Injector
|
|||
int dwSize,
|
||||
AllocationType dwFreeType);
|
||||
|
||||
/// <summary>
|
||||
/// Waits until the specified object is in the signaled state or the time-out interval elapses. To enter an alertable wait
|
||||
/// state, use the WaitForSingleObjectEx function.To wait for multiple objects, use WaitForMultipleObjects.
|
||||
/// </summary>
|
||||
/// <param name="hHandle">
|
||||
/// A handle to the object. For a list of the object types whose handles can be specified, see the following Remarks section.
|
||||
/// If this handle is closed while the wait is still pending, the function's behavior is undefined. The handle must have the
|
||||
/// SYNCHRONIZE access right. For more information, see Standard Access Rights.
|
||||
/// </param>
|
||||
/// <param name="dwMilliseconds">
|
||||
/// The time-out interval, in milliseconds. If a nonzero value is specified, the function waits until the object is signaled
|
||||
/// or the interval elapses. If dwMilliseconds is zero, the function does not enter a wait state if the object is not signaled;
|
||||
/// it always returns immediately. If dwMilliseconds is INFINITE, the function will return only when the object is signaled.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value indicates the event that caused the function to return.
|
||||
/// It can be one of the WaitResult values.
|
||||
/// </returns>
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to an area of memory in a specified process. The entire area to be written to must be accessible or
|
||||
/// the operation fails.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
79
Dalamud.Injector/RemotePinnedData.cs
Normal file
79
Dalamud.Injector/RemotePinnedData.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,96 +1,70 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\xunit.runner.console.2.4.1\build\xunit.runner.console.props" Condition="Exists('..\packages\xunit.runner.console.2.4.1\build\xunit.runner.console.props')" />
|
||||
<Import Project="..\packages\xunit.runner.visualstudio.2.4.3\build\net452\xunit.runner.visualstudio.props" Condition="Exists('..\packages\xunit.runner.visualstudio.2.4.3\build\net452\xunit.runner.visualstudio.props')" />
|
||||
<Import Project="..\packages\xunit.core.2.4.1\build\xunit.core.props" Condition="Exists('..\packages\xunit.core.2.4.1\build\xunit.core.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{C8004563-1806-4329-844F-0EF6274291FC}</ProjectGuid>
|
||||
<ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Dalamud.Test</RootNamespace>
|
||||
<AssemblyName>Dalamud.Test</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c">
|
||||
<HintPath>..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="xunit.assert, Version=2.4.1.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c">
|
||||
<HintPath>..\packages\xunit.assert.2.4.1\lib\netstandard1.1\xunit.assert.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="xunit.core, Version=2.4.1.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c">
|
||||
<HintPath>..\packages\xunit.extensibility.core.2.4.1\lib\net452\xunit.core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="xunit.execution.desktop, Version=2.4.1.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c">
|
||||
<HintPath>..\packages\xunit.extensibility.execution.2.4.1\lib\net452\xunit.execution.desktop.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="LocalizationTests.cs" />
|
||||
<Compile Include="Game\Text\Sanitizer\SanitizerTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Dalamud\Dalamud.csproj">
|
||||
<Project>{b92dab43-2279-4a2c-96e3-d9d5910edbea}</Project>
|
||||
<Name>Dalamud</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="..\packages\xunit.analyzers.0.10.0\analyzers\dotnet\cs\xunit.analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup Label="Target">
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Platforms>x64;AnyCPU</Platforms>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\xunit.core.2.4.1\build\xunit.core.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.core.2.4.1\build\xunit.core.props'))" />
|
||||
<Error Condition="!Exists('..\packages\xunit.core.2.4.1\build\xunit.core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.core.2.4.1\build\xunit.core.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\xunit.runner.visualstudio.2.4.3\build\net452\xunit.runner.visualstudio.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.runner.visualstudio.2.4.3\build\net452\xunit.runner.visualstudio.props'))" />
|
||||
<Error Condition="!Exists('..\packages\xunit.runner.console.2.4.1\build\xunit.runner.console.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.runner.console.2.4.1\build\xunit.runner.console.props'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\xunit.core.2.4.1\build\xunit.core.targets" Condition="Exists('..\packages\xunit.core.2.4.1\build\xunit.core.targets')" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
|
||||
<PropertyGroup Label="Feature">
|
||||
<RootNamespace>Dalamud.Test</RootNamespace>
|
||||
<AssemblyTitle>Dalamud.Test</AssemblyTitle>
|
||||
<AssemblyName>Dalamud.Test</AssemblyName>
|
||||
<Product>Dalamud.Test</Product>
|
||||
<Description>Unit tests for Dalamud</Description>
|
||||
<Company>goatcorp</Company>
|
||||
<Copyright>Copyright © goatcorp 2021</Copyright>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Output">
|
||||
<OutputType>Library</OutputType>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Configuration">
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Dalamud\Dalamud.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageReference Include="xunit.analyzers" Version="0.10.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.core" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.4.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
58
Dalamud.Test/Game/GameVersionTests.cs
Normal file
58
Dalamud.Test/Game/GameVersionTests.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +1,29 @@
|
|||
// ReSharper disable StringLiteralTypo
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Dalamud.Test.Game.Text.Sanitizer {
|
||||
|
||||
public class SanitizerTests {
|
||||
// ReSharper disable StringLiteralTypo
|
||||
namespace Dalamud.Test.Game.Text.Sanitizer
|
||||
{
|
||||
public class SanitizerTests
|
||||
{
|
||||
private global::Dalamud.Game.Text.Sanitizer.Sanitizer sanitizer;
|
||||
|
||||
[Theory]
|
||||
[InlineData( ClientLanguage.English, "Pixie Cotton Hood of Healing", "Pixie Cotton Hood of Healing" )]
|
||||
[InlineData( ClientLanguage.Japanese, "アラガントームストーン:真理", "アラガントームストーン:真理" )]
|
||||
[InlineData( ClientLanguage.German, "Anemos-Pan\x02\x16\x01\x03zer\x02\x16\x01\x03hand\x02\x16\x01\x03schu\x02\x16\x01\x03he des Drachenbluts", "Anemos-Panzerhandschuhe des Drachenbluts" )]
|
||||
[InlineData( ClientLanguage.German, "Bienen-Spatha †", "Bienen-Spatha" )]
|
||||
[InlineData( ClientLanguage.French, "Le Diademe\x02\x1D\x01\x03: terrains de chasse|Le Diademe\x02\x1D\x01\x03: terrains de chasse", "Le Diademe: terrains de chasse|Le Diademe: terrains de chasse" )]
|
||||
[InlineData( ClientLanguage.French, "Cuir de bœuf", "Cuir de boeuf" )]
|
||||
public void StringsAreSanitizedCorrectly(
|
||||
ClientLanguage clientLanguage, string unsanitizedString, string sanitizedString)
|
||||
[InlineData(ClientLanguage.English, "Pixie Cotton Hood of Healing", "Pixie Cotton Hood of Healing")]
|
||||
[InlineData(ClientLanguage.Japanese, "アラガントームストーン:真理", "アラガントームストーン:真理")]
|
||||
[InlineData(ClientLanguage.German, "Anemos-Pan\x02\x16\x01\x03zer\x02\x16\x01\x03hand\x02\x16\x01\x03schu\x02\x16\x01\x03he des Drachenbluts", "Anemos-Panzerhandschuhe des Drachenbluts")]
|
||||
[InlineData(ClientLanguage.German, "Bienen-Spatha †", "Bienen-Spatha")]
|
||||
[InlineData(ClientLanguage.French, "Le Diademe\x02\x1D\x01\x03: terrains de chasse|Le Diademe\x02\x1D\x01\x03: terrains de chasse", "Le Diademe: terrains de chasse|Le Diademe: terrains de chasse")]
|
||||
[InlineData(ClientLanguage.French, "Cuir de bœuf", "Cuir de boeuf")]
|
||||
public void StringsAreSanitizedCorrectly(ClientLanguage clientLanguage, string unsanitizedString, string sanitizedString)
|
||||
{
|
||||
var sanitizedStrings = new List<string> {unsanitizedString};
|
||||
var sanitizedStrings = new List<string> { unsanitizedString };
|
||||
|
||||
sanitizer = new global::Dalamud.Game.Text.Sanitizer.Sanitizer(clientLanguage);
|
||||
Assert.Equal(sanitizedString, sanitizer.Sanitize(unsanitizedString));
|
||||
Assert.Equal(sanitizedString, sanitizer.Sanitize(sanitizedStrings).First());
|
||||
|
||||
|
||||
sanitizer = new global::Dalamud.Game.Text.Sanitizer.Sanitizer(ClientLanguage.English);
|
||||
Assert.Equal(sanitizedString, sanitizer.Sanitize(unsanitizedString, clientLanguage));
|
||||
Assert.Equal(sanitizedString, sanitizer.Sanitize(sanitizedStrings, clientLanguage).First());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
using System.Linq;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Xunit;
|
||||
|
||||
namespace Dalamud.Test.Game.Text.SeStringHandling
|
||||
{
|
||||
public class SeStringManagerTests
|
||||
{
|
||||
[Fact]
|
||||
public void TestNewLinePayload()
|
||||
{
|
||||
var manager = new SeStringManager(null);
|
||||
var newLinePayloadBytes = new byte[] {0x02, 0x10, 0x01, 0x03};
|
||||
|
||||
var seString = manager.Parse(newLinePayloadBytes);
|
||||
|
||||
Assert.True(newLinePayloadBytes.SequenceEqual(seString.Encode()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,24 @@
|
|||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Xunit;
|
||||
|
||||
namespace Dalamud.Test {
|
||||
public class LocalizationTests {
|
||||
namespace Dalamud.Test
|
||||
{
|
||||
public class LocalizationTests
|
||||
{
|
||||
private readonly Localization localization;
|
||||
private string currentLangCode;
|
||||
|
||||
public LocalizationTests() {
|
||||
|
||||
public LocalizationTests()
|
||||
{
|
||||
var workingDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
this.localization = new Localization(workingDir, "dalamud_");
|
||||
this.localization.OnLocalizationChanged += code => this.currentLangCode = code;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetupWithFallbacks_EventInvoked() {
|
||||
public void SetupWithFallbacks_EventInvoked()
|
||||
{
|
||||
this.localization.SetupWithFallbacks();
|
||||
Assert.Equal("en", this.currentLangCode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
@ -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>
|
||||
222
Dalamud.sln
222
Dalamud.sln
|
|
@ -1,137 +1,163 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29215.179
|
||||
VisualStudioVersion = 16.0.31424.327
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CEF7D22B-CB85-400E-BD64-349A30E3C097}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
.gitignore = .gitignore
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "build", "build\build.csproj", "{94E5B016-02B1-459B-97D9-E783F28764B2}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud", "Dalamud\Dalamud.csproj", "{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dalamud.Boot", "Dalamud.Boot\Dalamud.Boot.vcxproj", "{55198DC3-A03D-408E-A8EB-2077780C8576}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.Injector", "Dalamud.Injector\Dalamud.Injector.csproj", "{5B832F73-5F54-4ADC-870F-D0095EF72C9A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImGuiScene", "lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj", "{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dalamud.Injector.Boot", "Dalamud.Injector.Boot\Dalamud.Injector.Boot.vcxproj", "{8874326B-E755-4D13-90B4-59AB263A3E6B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SDL2-CS", "lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj", "{85480198-8711-4355-830E-72FD794AD3F6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImGui.NET-472", "lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj", "{0483026E-C6CE-4B1A-AA68-46544C08140B}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.Test", "Dalamud.Test\Dalamud.Test.csproj", "{C8004563-1806-4329-844F-0EF6274291FC}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interface", "Interface", "{E15BDA6D-E881-4482-94BA-BE5527E917FF}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DalamudDebugStub", "DalamudDebugStub\DalamudDebugStub.vcxproj", "{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImGui.NET-472", "lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj", "{0483026E-C6CE-4B1A-AA68-46544C08140B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFXIVClientStructs", "lib\FFXIVClientStructs\FFXIVClientStructs.csproj", "{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImGuiScene", "lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj", "{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.Test", "Dalamud.Test\Dalamud.Test.csproj", "{C8004563-1806-4329-844F-0EF6274291FC}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SDL2-CS", "lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj", "{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.CorePlugin", "Dalamud.CorePlugin\Dalamud.CorePlugin.csproj", "{4AFDB34A-7467-4D41-B067-53BC4101D9D0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFXIVClientStructs", "lib\FFXIVClientStructs\FFXIVClientStructs\FFXIVClientStructs.csproj", "{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFXIVClientStructs.Common", "lib\FFXIVClientStructs\FFXIVClientStructs.Common\FFXIVClientStructs.Common.csproj", "{F3F0CC3A-DE2E-403F-88B4-B47C62582477}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFXIVClientStructs.Generators", "lib\FFXIVClientStructs\FFXIVClientStructs.Generators\FFXIVClientStructs.Generators.csproj", "{05AB2F46-268B-4915-806F-DDF813E2D59D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{94E5B016-02B1-459B-97D9-E783F28764B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{94E5B016-02B1-459B-97D9-E783F28764B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{94E5B016-02B1-459B-97D9-E783F28764B2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{94E5B016-02B1-459B-97D9-E783F28764B2}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{94E5B016-02B1-459B-97D9-E783F28764B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{94E5B016-02B1-459B-97D9-E783F28764B2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{94E5B016-02B1-459B-97D9-E783F28764B2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{94E5B016-02B1-459B-97D9-E783F28764B2}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|x64.Build.0 = Debug|x64
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|Any CPU.Build.0 = Release|x64
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|x64.ActiveCfg = Release|x64
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|x64.Build.0 = Release|x64
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{B92DAB43-2279-4A2C-96E3-D9D5910EDBEA}.Release|x86.Build.0 = Release|Any CPU
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{55198DC3-A03D-408E-A8EB-2077780C8576}.Debug|x64.Build.0 = Debug|x64
|
||||
{55198DC3-A03D-408E-A8EB-2077780C8576}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{55198DC3-A03D-408E-A8EB-2077780C8576}.Release|Any CPU.Build.0 = Release|x64
|
||||
{55198DC3-A03D-408E-A8EB-2077780C8576}.Release|x64.ActiveCfg = Release|x64
|
||||
{55198DC3-A03D-408E-A8EB-2077780C8576}.Release|x64.Build.0 = Release|x64
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|x64.Build.0 = Debug|x64
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|Any CPU.Build.0 = Release|x64
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x64.ActiveCfg = Release|x64
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x64.Build.0 = Release|x64
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|x64.Build.0 = Debug|x64
|
||||
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{85480198-8711-4355-830E-72FD794AD3F6}.Debug|x86.Build.0 = Debug|x86
|
||||
{85480198-8711-4355-830E-72FD794AD3F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{85480198-8711-4355-830E-72FD794AD3F6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{85480198-8711-4355-830E-72FD794AD3F6}.Release|x64.ActiveCfg = Release|x64
|
||||
{85480198-8711-4355-830E-72FD794AD3F6}.Release|x64.Build.0 = Release|x64
|
||||
{85480198-8711-4355-830E-72FD794AD3F6}.Release|x86.ActiveCfg = Release|x86
|
||||
{85480198-8711-4355-830E-72FD794AD3F6}.Release|x86.Build.0 = Release|x86
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|Any CPU.Build.0 = Release|x64
|
||||
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|x64.ActiveCfg = Release|x64
|
||||
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|x64.Build.0 = Release|x64
|
||||
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|x86.ActiveCfg = Release|Win32
|
||||
{9FDA9A5C-50C6-4333-8DCE-DFEB89363F2A}.Release|x86.Build.0 = Release|Win32
|
||||
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3DBAEE68-9D94-4807-BCB1-E42EDD52B489}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x86.Build.0 = Release|Any CPU
|
||||
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|x64.Build.0 = Debug|x64
|
||||
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|x64.ActiveCfg = Release|x64
|
||||
{8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|x64.Build.0 = Release|x64
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|x64.Build.0 = Debug|x64
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.Build.0 = Release|x64
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x64.ActiveCfg = Release|x64
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Release|x64.Build.0 = Release|x64
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.Build.0 = Debug|x64
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.Build.0 = Release|x64
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.ActiveCfg = Release|x64
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.Build.0 = Release|x64
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|x64.Build.0 = Debug|x64
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.Build.0 = Release|x64
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x64.ActiveCfg = Release|x64
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|x64.Build.0 = Release|x64
|
||||
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|x64.Build.0 = Debug|x64
|
||||
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|Any CPU.Build.0 = Release|x64
|
||||
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|x64.ActiveCfg = Release|x64
|
||||
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|x64.Build.0 = Release|x64
|
||||
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F3F0CC3A-DE2E-403F-88B4-B47C62582477}.Release|x64.Build.0 = Release|Any CPU
|
||||
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{05AB2F46-268B-4915-806F-DDF813E2D59D}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
|
||||
{85480198-8711-4355-830E-72FD794AD3F6} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
|
||||
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
|
||||
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
|
||||
{F3F0CC3A-DE2E-403F-88B4-B47C62582477} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
|
||||
{05AB2F46-268B-4915-806F-DDF813E2D59D} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {79B65AC9-C940-410E-AB61-7EA7E12C7599}
|
||||
|
|
|
|||
|
|
@ -5,20 +5,21 @@ using System.IO;
|
|||
using Dalamud.Game.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace Dalamud.Configuration
|
||||
namespace Dalamud.Configuration.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Class containing Dalamud settings.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal class DalamudConfiguration
|
||||
internal sealed class DalamudConfiguration
|
||||
{
|
||||
[JsonIgnore]
|
||||
private string configPath;
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for the <see cref="DalamudConfiguration.OnDalamudConfigurationSaved"/> event that occurs when the dalamud configuration is saved.
|
||||
/// Delegate for the <see cref="OnDalamudConfigurationSaved"/> event that occurs when the dalamud configuration is saved.
|
||||
/// </summary>
|
||||
/// <param name="dalamudConfiguration">The current dalamud configuration.</param>
|
||||
public delegate void DalamudConfigurationSavedDelegate(DalamudConfiguration dalamudConfiguration);
|
||||
|
|
@ -68,15 +69,28 @@ namespace Dalamud.Configuration
|
|||
/// </summary>
|
||||
public bool DoDalamudTest { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not XL should download the Dalamud .NET runtime.
|
||||
/// </summary>
|
||||
public bool DoDalamudRuntime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of custom repos.
|
||||
/// </summary>
|
||||
public List<ThirdRepoSetting> ThirdRepoList { get; set; } = new List<ThirdRepoSetting>();
|
||||
public List<ThirdPartyRepoSettings> ThirdRepoList { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of hidden plugins.
|
||||
/// </summary>
|
||||
public List<string> HiddenPluginInternalName { get; set; } = new List<string>();
|
||||
public List<string> HiddenPluginInternalName { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of additional settings for devPlugins. The key is the absolute path
|
||||
/// to the 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 Dictionary<string, DevPluginSettings> DevPluginSettings { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the global UI scale.
|
||||
|
|
@ -113,6 +127,11 @@ namespace Dalamud.Configuration
|
|||
/// </summary>
|
||||
public bool DoButtonsSystemMenu { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default Dalamud debug log level on startup.
|
||||
/// </summary>
|
||||
public LogEventLevel LogLevel { get; set; } = LogEventLevel.Information;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not the debug log should scroll automatically.
|
||||
/// </summary>
|
||||
|
|
@ -138,6 +157,21 @@ namespace Dalamud.Configuration
|
|||
/// </summary>
|
||||
public bool IsGamepadNavigationEnabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not the anti-anti-debug check is enabled on startup.
|
||||
/// </summary>
|
||||
public bool IsAntiAntiDebugEnabled { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the kind of beta to download when <see cref="DoDalamudTest"/> is set to true.
|
||||
/// </summary>
|
||||
public string DalamudBetaKind { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not all plugins, regardless of API level, should be loaded.
|
||||
/// </summary>
|
||||
public bool LoadAllApiLevels { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Load a configuration from the provided path.
|
||||
/// </summary>
|
||||
18
Dalamud/Configuration/Internal/DevPluginSettings.cs
Normal file
18
Dalamud/Configuration/Internal/DevPluginSettings.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
namespace Dalamud.Configuration.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings for DevPlugins.
|
||||
/// </summary>
|
||||
internal sealed class DevPluginSettings
|
||||
{
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ namespace Dalamud.Configuration
|
|||
/// <summary>
|
||||
/// Third party repository for dalamud plugins.
|
||||
/// </summary>
|
||||
internal class ThirdRepoSetting
|
||||
internal sealed class ThirdPartyRepoSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the third party repo url.
|
||||
|
|
@ -16,16 +16,14 @@ namespace Dalamud.Configuration
|
|||
public bool IsEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create new instance of third party repo object.
|
||||
/// Gets or sets a short name for the repo url.
|
||||
/// </summary>
|
||||
/// <returns>New instance of third party repo.</returns>
|
||||
public ThirdRepoSetting Clone()
|
||||
{
|
||||
return new ThirdRepoSetting
|
||||
{
|
||||
Url = this.Url,
|
||||
IsEnabled = this.IsEnabled,
|
||||
};
|
||||
}
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Clone this object.
|
||||
/// </summary>
|
||||
/// <returns>A shallow copy of this object.</returns>
|
||||
public ThirdPartyRepoSettings Clone() => this.MemberwiseClone() as ThirdPartyRepoSettings;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System.IO;
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Dalamud.Configuration
|
||||
|
|
@ -7,7 +8,7 @@ namespace Dalamud.Configuration
|
|||
/// <summary>
|
||||
/// Configuration to store settings for a dalamud plugin.
|
||||
/// </summary>
|
||||
public class PluginConfigurations
|
||||
public sealed class PluginConfigurations
|
||||
{
|
||||
private readonly DirectoryInfo configDirectory;
|
||||
|
||||
|
|
@ -44,6 +45,7 @@ namespace Dalamud.Configuration
|
|||
/// </summary>
|
||||
/// <param name="pluginName">Plugin name.</param>
|
||||
/// <returns>Plugin configuration.</returns>
|
||||
[CanBeNull]
|
||||
public IPluginConfiguration Load(string pluginName)
|
||||
{
|
||||
var path = this.GetConfigFile(pluginName);
|
||||
|
|
@ -60,6 +62,22 @@ namespace Dalamud.Configuration
|
|||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete the configuration file and folder for the specified plugin.
|
||||
/// This will throw an <see cref="IOException"/> if the plugin did not correctly close its handles.
|
||||
/// </summary>
|
||||
/// <param name="pluginName">The name of the plugin.</param>
|
||||
public void Delete(string pluginName)
|
||||
{
|
||||
var directory = this.GetDirectoryPath(pluginName);
|
||||
if (directory.Exists)
|
||||
directory.Delete(true);
|
||||
|
||||
var file = this.GetConfigFile(pluginName);
|
||||
if (file.Exists)
|
||||
file.Delete();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get plugin directory.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Configuration;
|
||||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.Addon;
|
||||
|
|
@ -13,17 +13,24 @@ using Dalamud.Game.Command;
|
|||
using Dalamud.Game.Internal;
|
||||
using Dalamud.Game.Network;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Hooking.Internal;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Memory;
|
||||
using Dalamud.Plugin.Internal;
|
||||
using Serilog;
|
||||
using Serilog.Core;
|
||||
|
||||
#if DEBUG
|
||||
// This allows for rapid prototyping of Dalamud modules with access to internal objects.
|
||||
[assembly: InternalsVisibleTo("Dalamud.CorePlugin")]
|
||||
#endif
|
||||
|
||||
namespace Dalamud
|
||||
{
|
||||
/// <summary>
|
||||
/// The main Dalamud class containing all subsystems.
|
||||
/// </summary>
|
||||
public sealed class Dalamud : IDisposable
|
||||
internal sealed class Dalamud : IDisposable
|
||||
{
|
||||
#region Internals
|
||||
|
||||
|
|
@ -31,8 +38,6 @@ namespace Dalamud
|
|||
|
||||
private readonly ManualResetEvent finishUnloadSignal;
|
||||
|
||||
private readonly string baseDirectory;
|
||||
|
||||
private bool hasDisposedPlugins = false;
|
||||
|
||||
#endregion
|
||||
|
|
@ -43,12 +48,17 @@ namespace Dalamud
|
|||
/// <param name="info">DalamudStartInfo instance.</param>
|
||||
/// <param name="loggingLevelSwitch">LoggingLevelSwitch to control Serilog level.</param>
|
||||
/// <param name="finishSignal">Signal signalling shutdown.</param>
|
||||
public Dalamud(DalamudStartInfo info, LoggingLevelSwitch loggingLevelSwitch, ManualResetEvent finishSignal)
|
||||
/// <param name="configuration">The Dalamud configuration.</param>
|
||||
public Dalamud(DalamudStartInfo info, LoggingLevelSwitch loggingLevelSwitch, ManualResetEvent finishSignal, DalamudConfiguration configuration)
|
||||
{
|
||||
#if DEBUG
|
||||
Instance = this;
|
||||
#endif
|
||||
this.StartInfo = info;
|
||||
this.LogLevelSwitch = loggingLevelSwitch;
|
||||
this.Configuration = configuration;
|
||||
|
||||
this.baseDirectory = info.WorkingDirectory;
|
||||
// this.baseDirectory = info.WorkingDirectory;
|
||||
|
||||
this.unloadSignal = new ManualResetEvent(false);
|
||||
this.unloadSignal.Reset();
|
||||
|
|
@ -57,6 +67,13 @@ namespace Dalamud
|
|||
this.finishUnloadSignal.Reset();
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// Gets the Dalamud singleton instance.
|
||||
/// </summary>
|
||||
internal static Dalamud Instance { get; private set; }
|
||||
#endif
|
||||
|
||||
#region Native Game Subsystems
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -74,6 +91,11 @@ namespace Dalamud
|
|||
/// </summary>
|
||||
internal WinSockHandlers WinSock2 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets Hook management subsystem.
|
||||
/// </summary>
|
||||
internal HookManager HookManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets ImGui Interface subsystem.
|
||||
/// </summary>
|
||||
|
|
@ -93,11 +115,6 @@ namespace Dalamud
|
|||
/// </summary>
|
||||
internal PluginManager PluginManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets Plugin Repository subsystem.
|
||||
/// </summary>
|
||||
internal PluginRepository PluginRepository { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets Data provider subsystem.
|
||||
/// </summary>
|
||||
|
|
@ -203,6 +220,7 @@ namespace Dalamud
|
|||
// Initialize the process information.
|
||||
this.TargetModule = Process.GetCurrentProcess().MainModule;
|
||||
this.SigScanner = new SigScanner(this.TargetModule, true);
|
||||
this.HookManager = new HookManager(this);
|
||||
|
||||
// Initialize game subsystem
|
||||
this.Framework = new Framework(this.SigScanner, this);
|
||||
|
|
@ -211,6 +229,10 @@ namespace Dalamud
|
|||
|
||||
this.Framework.Enable();
|
||||
Log.Information("[T1] Framework ENABLE!");
|
||||
|
||||
// Initialize FFXIVClientStructs function resolver
|
||||
FFXIVClientStructs.Resolver.Initialize();
|
||||
Log.Information("[T1] FFXIVClientStructs initialized!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -226,11 +248,12 @@ namespace Dalamud
|
|||
{
|
||||
try
|
||||
{
|
||||
this.Configuration = DalamudConfiguration.Load(this.StartInfo.ConfigurationPath);
|
||||
|
||||
this.AntiDebug = new AntiDebug(this.SigScanner);
|
||||
if (this.Configuration.IsAntiAntiDebugEnabled)
|
||||
this.AntiDebug.Enable();
|
||||
#if DEBUG
|
||||
this.AntiDebug.Enable();
|
||||
if (!this.AntiDebug.IsEnabled)
|
||||
this.AntiDebug.Enable();
|
||||
#endif
|
||||
|
||||
Log.Information("[T2] AntiDebug OK!");
|
||||
|
|
@ -286,6 +309,7 @@ namespace Dalamud
|
|||
Log.Information("[T2] Data OK!");
|
||||
|
||||
this.SeStringManager = new SeStringManager(this.Data);
|
||||
MemoryHelper.Initialize(this); // For SeString handling
|
||||
|
||||
Log.Information("[T2] SeString OK!");
|
||||
|
||||
|
|
@ -324,28 +348,21 @@ namespace Dalamud
|
|||
{
|
||||
Log.Information("[T3] START!");
|
||||
|
||||
this.PluginRepository =
|
||||
new PluginRepository(this, this.StartInfo.PluginDirectory, this.StartInfo.GameVersion);
|
||||
|
||||
Log.Information("[T3] PREPO OK!");
|
||||
|
||||
if (!bool.Parse(Environment.GetEnvironmentVariable("DALAMUD_NOT_HAVE_PLUGINS") ?? "false"))
|
||||
{
|
||||
try
|
||||
{
|
||||
this.PluginRepository.CleanupPlugins();
|
||||
|
||||
Log.Information("[T3] PRC OK!");
|
||||
|
||||
this.PluginManager = new PluginManager(
|
||||
this,
|
||||
this.StartInfo.PluginDirectory,
|
||||
this.StartInfo.DefaultPluginDirectory);
|
||||
this.PluginManager.LoadSynchronousPlugins();
|
||||
|
||||
Task.Run(() => this.PluginManager.LoadDeferredPlugins());
|
||||
this.PluginManager = new PluginManager(this);
|
||||
this.PluginManager.OnInstalledPluginsChanged += () =>
|
||||
Troubleshooting.LogTroubleshooting(this, this.InterfaceManager.IsReady);
|
||||
|
||||
Log.Information("[T3] PM OK!");
|
||||
|
||||
this.PluginManager.CleanupPlugins();
|
||||
Log.Information("[T3] PMC OK!");
|
||||
|
||||
this.PluginManager.LoadAllPlugins();
|
||||
Log.Information("[T3] PML OK!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -354,11 +371,9 @@ namespace Dalamud
|
|||
}
|
||||
|
||||
this.DalamudUi = new DalamudInterface(this);
|
||||
this.InterfaceManager.OnDraw += this.DalamudUi.Draw;
|
||||
|
||||
Log.Information("[T3] DUI OK!");
|
||||
|
||||
Troubleshooting.LogTroubleshooting(this, this.InterfaceManager != null);
|
||||
Troubleshooting.LogTroubleshooting(this, this.InterfaceManager.IsReady);
|
||||
|
||||
Log.Information("Dalamud is ready.");
|
||||
}
|
||||
|
|
@ -407,16 +422,9 @@ namespace Dalamud
|
|||
// use any resources that it freed in its own Dispose method
|
||||
this.InterfaceManager?.Dispose();
|
||||
|
||||
try
|
||||
{
|
||||
this.PluginManager.UnloadPlugins();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Plugin unload failed.");
|
||||
}
|
||||
|
||||
this.DalamudUi?.Dispose();
|
||||
|
||||
this.PluginManager?.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -433,20 +441,23 @@ namespace Dalamud
|
|||
}
|
||||
|
||||
this.Framework?.Dispose();
|
||||
|
||||
this.ClientState?.Dispose();
|
||||
|
||||
this.unloadSignal?.Dispose();
|
||||
|
||||
this.WinSock2?.Dispose();
|
||||
|
||||
this.SigScanner?.Dispose();
|
||||
|
||||
this.Data?.Dispose();
|
||||
|
||||
this.AntiDebug?.Dispose();
|
||||
|
||||
this.SystemMenu?.Dispose();
|
||||
|
||||
this.HookManager?.Dispose();
|
||||
|
||||
this.SigScanner?.Dispose();
|
||||
|
||||
Log.Debug("Dalamud::Dispose() OK!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
|||
|
|
@ -1,105 +1,124 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup Label="Target">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Platforms>x64;AnyCPU</Platforms>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Build">
|
||||
<OutputType>Library</OutputType>
|
||||
<OutputPath></OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<DocumentationFile>$(SolutionDir)\bin\Dalamud.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Feature">
|
||||
<DalamudVersion>5.2.6.1</DalamudVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DalamudVersion>6.0.0.0</DalamudVersion>
|
||||
<Description>XIV Launcher addon framework</Description>
|
||||
<AssemblyVersion>$(DalamudVersion)</AssemblyVersion>
|
||||
<Version>$(DalamudVersion)</Version>
|
||||
<FileVersion>$(DalamudVersion)</FileVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Resources">
|
||||
<None Include="$(SolutionDir)/Resources/**/*" CopyToOutputDirectory="PreserveNewest" Visible="false" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
<AppOutputBase>$(MSBuildProjectDirectory)\</AppOutputBase>
|
||||
<PathMap>$(AppOutputBase)=C:\goatsoft\companysecrets\dalamud\</PathMap>
|
||||
|
||||
<PropertyGroup Label="Output">
|
||||
<OutputType>Library</OutputType>
|
||||
<OutputPath>..\bin\$(Configuration)\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Documentation">
|
||||
<DocumentationFile>$(OutputPath)Dalamud.xml</DocumentationFile>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Build">
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<Deterministic>true</Deterministic>
|
||||
<Nullable>annotations</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
|
||||
<PropertyGroup Label="Configuration">
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)'=='Debug'">
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<NoWarn>IDE0017;IDE0044;IDE0047;IDE0048;IDE1006;CS1573;CS1591;CS1701;CS1702</NoWarn>
|
||||
<!-- IDE0017 - Use object initializers -->
|
||||
<!-- IDE0044 - Add readonly modifier -->
|
||||
<!-- IDE0047 - Parentheses preferences -->
|
||||
<!-- IDE0048 - Parentheses preferences -->
|
||||
<!-- IDE1006 - Naming preferences -->
|
||||
<!-- CS1573 - Parameter has no matching param tag in the XML comment -->
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)'=='Release'">
|
||||
<AppOutputBase>$(MSBuildProjectDirectory)\</AppOutputBase>
|
||||
<PathMap>$(AppOutputBase)=C:\goatsoft\companysecrets\dalamud\</PathMap>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Warnings">
|
||||
<NoWarn>IDE0003;IDE0044;IDE1006;CS1591;CS1701;CS1702</NoWarn>
|
||||
<!-- IDE1006 - Naming violation -->
|
||||
<!-- CS1591 - Missing XML comment for publicly visible type or member -->
|
||||
<!-- CS1701 - Runtime policy may be needed -->
|
||||
<!-- CS1702 - Runtime policy may be needed -->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Resources\Lumina.Generated.dll" />
|
||||
<None Remove="stylecop.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="stylecop.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CheapLoc" Version="1.1.3" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" />
|
||||
<PackageReference Include="Lumina" Version="3.1.0" />
|
||||
<PackageReference Include="CheapLoc" Version="1.1.5" />
|
||||
<PackageReference Include="CoreHook" Version="1.0.4" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2021.1.0" />
|
||||
<PackageReference Include="Lib.Harmony" Version="2.1.0" />
|
||||
<PackageReference Include="Lumina" Version="3.3.0" />
|
||||
<PackageReference Include="Lumina.Excel" Version="5.50.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="2.6.1" />
|
||||
<PackageReference Include="Serilog" Version="2.6.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="4.0.0" />
|
||||
<PackageReference Include="EasyHook" Version="2.7.6270" />
|
||||
<PackageReference Include="SharpDX.Desktop" Version="4.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.10.0" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.333">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="5.0.2" />
|
||||
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="5.0.1" />
|
||||
<PackageReference Include="System.Resources.Extensions" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Net.Http" />
|
||||
<ProjectReference Include="..\lib\FFXIVClientStructs\FFXIVClientStructs\FFXIVClientStructs.csproj" />
|
||||
<ProjectReference Include="..\lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj" />
|
||||
<ProjectReference Include="..\lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj" />
|
||||
<ProjectReference Include="..\lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="..\stylecop.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\lib\FFXIVClientStructs\FFXIVClientStructs.csproj" />
|
||||
<ProjectReference Include="..\lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj" />
|
||||
<ProjectReference Include="..\lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj" />
|
||||
<ProjectReference Include="..\lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ContentWithTargetPath Include="Resources\Lumina.Generated.dll">
|
||||
<None Include="corehook64.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<TargetPath>Lumina.Generated.dll</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="AddRuntimeDependenciesToContent" BeforeTargets="GetCopyToOutputDirectoryItems" DependsOnTargets="GenerateBuildDependencyFile;GenerateBuildRuntimeConfigurationFiles">
|
||||
<ItemGroup>
|
||||
<ContentWithTargetPath Include="$(ProjectDepsFilePath)" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectDepsFileName)" />
|
||||
<ContentWithTargetPath Include="$(ProjectRuntimeConfigFilePath)" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectRuntimeConfigFileName)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="GetGitHash" BeforeTargets="WriteGitHash" Condition="'$(BuildHash)' == ''">
|
||||
<PropertyGroup>
|
||||
<!-- temp file for the git version (lives in "obj" folder)-->
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
using System;
|
||||
|
||||
using Dalamud.Game;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Dalamud
|
||||
{
|
||||
/// <summary>
|
||||
/// Class containing information needed to initialize Dalamud.
|
||||
/// Struct containing information needed to initialize Dalamud.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class DalamudStartInfo
|
||||
public struct DalamudStartInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The working directory of the XIVLauncher installations.
|
||||
|
|
@ -41,7 +44,8 @@ namespace Dalamud
|
|||
/// <summary>
|
||||
/// The current game version code.
|
||||
/// </summary>
|
||||
public string GameVersion;
|
||||
[JsonConverter(typeof(GameVersionConverter))]
|
||||
public GameVersion GameVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not market board information should be uploaded by default.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Threading;
|
|||
|
||||
using Dalamud.Data.LuminaExtensions;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Internal;
|
||||
using ImGuiScene;
|
||||
using JetBrains.Annotations;
|
||||
using Lumina;
|
||||
|
|
@ -21,7 +22,7 @@ namespace Dalamud.Data
|
|||
/// <summary>
|
||||
/// This class provides data for Dalamud-internal features, but can also be used by plugins if needed.
|
||||
/// </summary>
|
||||
public class DataManager : IDisposable
|
||||
public sealed class DataManager : IDisposable
|
||||
{
|
||||
private const string IconFileFormat = "ui/icon/{0:D3}000/{1}{2:D6}.tex";
|
||||
private readonly InterfaceManager interfaceManager;
|
||||
|
|
@ -32,6 +33,7 @@ namespace Dalamud.Data
|
|||
private GameData gameData;
|
||||
|
||||
private Thread luminaResourceThread;
|
||||
private CancellationTokenSource luminaCancellationTokenSource;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataManager"/> class.
|
||||
|
|
@ -41,8 +43,9 @@ namespace Dalamud.Data
|
|||
internal DataManager(ClientLanguage language, InterfaceManager interfaceManager)
|
||||
{
|
||||
this.interfaceManager = interfaceManager;
|
||||
|
||||
// Set up default values so plugins do not null-reference when data is being loaded.
|
||||
this.ServerOpCodes = new ReadOnlyDictionary<string, ushort>(new Dictionary<string, ushort>());
|
||||
this.ClientOpCodes = this.ServerOpCodes = new ReadOnlyDictionary<string, ushort>(new Dictionary<string, ushort>());
|
||||
|
||||
this.Language = language;
|
||||
}
|
||||
|
|
@ -99,7 +102,7 @@ namespace Dalamud.Data
|
|||
ClientLanguage.English => Lumina.Data.Language.English,
|
||||
ClientLanguage.German => Lumina.Data.Language.German,
|
||||
ClientLanguage.French => Lumina.Data.Language.French,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(this.Language), $"Unknown Language: {this.Language}"),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(language), $"Unknown Language: {language}"),
|
||||
};
|
||||
return this.Excel.GetSheet<T>(lang);
|
||||
}
|
||||
|
|
@ -162,7 +165,7 @@ namespace Dalamud.Data
|
|||
ClientLanguage.English => "en/",
|
||||
ClientLanguage.German => "de/",
|
||||
ClientLanguage.French => "fr/",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(this.Language), $"Unknown Language: {this.Language}"),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(iconLanguage), $"Unknown Language: {iconLanguage}"),
|
||||
};
|
||||
|
||||
return this.GetIcon(type, iconId);
|
||||
|
|
@ -232,7 +235,7 @@ namespace Dalamud.Data
|
|||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
this.luminaResourceThread.Abort();
|
||||
this.luminaCancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -245,14 +248,14 @@ namespace Dalamud.Data
|
|||
{
|
||||
Log.Verbose("Starting data load...");
|
||||
|
||||
var zoneOpCodeDict =
|
||||
JsonConvert.DeserializeObject<Dictionary<string, ushort>>(File.ReadAllText(Path.Combine(baseDir, "UIRes", "serveropcode.json")));
|
||||
var zoneOpCodeDict = JsonConvert.DeserializeObject<Dictionary<string, ushort>>(
|
||||
File.ReadAllText(Path.Combine(baseDir, "UIRes", "serveropcode.json")));
|
||||
this.ServerOpCodes = new ReadOnlyDictionary<string, ushort>(zoneOpCodeDict);
|
||||
|
||||
Log.Verbose("Loaded {0} ServerOpCodes.", zoneOpCodeDict.Count);
|
||||
|
||||
var clientOpCodeDict =
|
||||
JsonConvert.DeserializeObject<Dictionary<string, ushort>>(File.ReadAllText(Path.Combine(baseDir, "UIRes", "clientopcode.json")));
|
||||
var clientOpCodeDict = JsonConvert.DeserializeObject<Dictionary<string, ushort>>(
|
||||
File.ReadAllText(Path.Combine(baseDir, "UIRes", "clientopcode.json")));
|
||||
this.ClientOpCodes = new ReadOnlyDictionary<string, ushort>(clientOpCodeDict);
|
||||
|
||||
Log.Verbose("Loaded {0} ClientOpCodes.", clientOpCodeDict.Count);
|
||||
|
|
@ -273,9 +276,7 @@ namespace Dalamud.Data
|
|||
ClientLanguage.English => Lumina.Data.Language.English,
|
||||
ClientLanguage.German => Lumina.Data.Language.German,
|
||||
ClientLanguage.French => Lumina.Data.Language.French,
|
||||
_ => throw new ArgumentOutOfRangeException(
|
||||
nameof(this.Language),
|
||||
@"Unknown Language: " + this.Language),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(this.Language), $"Unknown Language: {this.Language}"),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -289,9 +290,12 @@ namespace Dalamud.Data
|
|||
|
||||
this.IsDataReady = true;
|
||||
|
||||
this.luminaResourceThread = new Thread(() =>
|
||||
this.luminaCancellationTokenSource = new();
|
||||
|
||||
var luminaCancellationToken = this.luminaCancellationTokenSource.Token;
|
||||
this.luminaResourceThread = new(() =>
|
||||
{
|
||||
while (true)
|
||||
while (!luminaCancellationToken.IsCancellationRequested)
|
||||
{
|
||||
if (this.gameData.FileHandleManager.HasPendingFileLoads)
|
||||
{
|
||||
|
|
@ -302,8 +306,6 @@ namespace Dalamud.Data
|
|||
Thread.Sleep(5);
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once FunctionNeverReturns
|
||||
});
|
||||
this.luminaResourceThread.Start();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Interface;
|
||||
using EasyHook;
|
||||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
|
|
@ -15,28 +17,41 @@ namespace Dalamud
|
|||
/// <summary>
|
||||
/// The main entrypoint for the Dalamud system.
|
||||
/// </summary>
|
||||
public sealed class EntryPoint : IEntryPoint
|
||||
public sealed class EntryPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EntryPoint"/> class.
|
||||
/// A delegate used during initialization of the CLR from Dalamud.Boot.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The <see cref="RemoteHooking.IContext"/> used to load the DLL.</param>
|
||||
/// <param name="info">The <see cref="DalamudStartInfo"/> containing information needed to initialize Dalamud.</param>
|
||||
public EntryPoint(RemoteHooking.IContext ctx, DalamudStartInfo info)
|
||||
/// <param name="infoPtr">Pointer to a serialized <see cref="DalamudStartInfo"/> data.</param>
|
||||
public delegate void InitDelegate(IntPtr infoPtr);
|
||||
|
||||
/// <summary>
|
||||
/// Initialize Dalamud.
|
||||
/// </summary>
|
||||
/// <param name="infoPtr">Pointer to a serialized <see cref="DalamudStartInfo"/> data.</param>
|
||||
public static void Initialize(IntPtr infoPtr)
|
||||
{
|
||||
// Required by EasyHook
|
||||
var infoStr = Marshal.PtrToStringAnsi(infoPtr);
|
||||
var info = JsonConvert.DeserializeObject<DalamudStartInfo>(infoStr);
|
||||
|
||||
new Thread(() => RunThread(info)).Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize all Dalamud subsystems and start running on the main thread.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The <see cref="RemoteHooking.IContext"/> used to load the DLL.</param>
|
||||
/// <param name="info">The <see cref="DalamudStartInfo"/> containing information needed to initialize Dalamud.</param>
|
||||
public void Run(RemoteHooking.IContext ctx, DalamudStartInfo info)
|
||||
private static void RunThread(DalamudStartInfo info)
|
||||
{
|
||||
// Load configuration first to get some early persistent state, like log level
|
||||
var configuration = DalamudConfiguration.Load(info.ConfigurationPath);
|
||||
|
||||
// Setup logger
|
||||
var (logger, levelSwitch) = this.NewLogger(info.WorkingDirectory);
|
||||
Log.Logger = logger;
|
||||
var levelSwitch = InitLogging(info.WorkingDirectory, configuration);
|
||||
|
||||
// Log any unhandled exception.
|
||||
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
|
||||
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
|
||||
|
||||
var finishSignal = new ManualResetEvent(false);
|
||||
|
||||
|
|
@ -46,14 +61,9 @@ namespace Dalamud
|
|||
Log.Information("Initializing a session..");
|
||||
|
||||
// This is due to GitHub not supporting TLS 1.0, so we enable all TLS versions globally
|
||||
System.Net.ServicePointManager.SecurityProtocol =
|
||||
SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls | SecurityProtocolType.Ssl3;
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls;
|
||||
|
||||
// Log any unhandled exception.
|
||||
AppDomain.CurrentDomain.UnhandledException += this.OnUnhandledException;
|
||||
TaskScheduler.UnobservedTaskException += this.OnUnobservedTaskException;
|
||||
|
||||
var dalamud = new Dalamud(info, levelSwitch, finishSignal);
|
||||
var dalamud = new Dalamud(info, levelSwitch, finishSignal, configuration);
|
||||
Log.Information("Starting a session..");
|
||||
|
||||
// Run session
|
||||
|
|
@ -68,7 +78,8 @@ namespace Dalamud
|
|||
}
|
||||
finally
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException -= this.OnUnhandledException;
|
||||
TaskScheduler.UnobservedTaskException -= OnUnobservedTaskException;
|
||||
AppDomain.CurrentDomain.UnhandledException -= OnUnhandledException;
|
||||
|
||||
Log.Information("Session has ended.");
|
||||
Log.CloseAndFlush();
|
||||
|
|
@ -77,7 +88,7 @@ namespace Dalamud
|
|||
}
|
||||
}
|
||||
|
||||
private (Logger Logger, LoggingLevelSwitch LevelSwitch) NewLogger(string baseDirectory)
|
||||
private static LoggingLevelSwitch InitLogging(string baseDirectory, DalamudConfiguration configuration)
|
||||
{
|
||||
#if DEBUG
|
||||
var logPath = Path.Combine(baseDirectory, "dalamud.log");
|
||||
|
|
@ -90,35 +101,34 @@ namespace Dalamud
|
|||
#if DEBUG
|
||||
levelSwitch.MinimumLevel = LogEventLevel.Verbose;
|
||||
#else
|
||||
levelSwitch.MinimumLevel = LogEventLevel.Information;
|
||||
levelSwitch.MinimumLevel = configuration.LogLevel;
|
||||
#endif
|
||||
|
||||
var newLogger = new LoggerConfiguration()
|
||||
.WriteTo.Async(a => a.File(logPath))
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.WriteTo.Async(a => a.File(logPath, fileSizeLimitBytes: 5 * 1024 * 1024, rollOnFileSizeLimit: true))
|
||||
.WriteTo.Sink(SerilogEventSink.Instance)
|
||||
.MinimumLevel.ControlledBy(levelSwitch)
|
||||
.CreateLogger();
|
||||
|
||||
return (newLogger, levelSwitch);
|
||||
return levelSwitch;
|
||||
}
|
||||
|
||||
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs arg)
|
||||
private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs args)
|
||||
{
|
||||
switch (arg.ExceptionObject)
|
||||
switch (args.ExceptionObject)
|
||||
{
|
||||
case Exception ex:
|
||||
Log.Fatal(ex, "Unhandled exception on AppDomain");
|
||||
break;
|
||||
default:
|
||||
Log.Fatal("Unhandled SEH object on AppDomain: {Object}", arg.ExceptionObject);
|
||||
Log.Fatal("Unhandled SEH object on AppDomain: {Object}", args.ExceptionObject);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
|
||||
private static void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs args)
|
||||
{
|
||||
if (!e.Observed)
|
||||
Log.Error(e.Exception, "Unobserved exception in Task.");
|
||||
if (!args.Observed)
|
||||
Log.Error(args.Exception, "Unobserved exception in Task.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,16 @@
|
|||
<xs:documentation>Used to control if the On_PropertyName_Changed feature is enabled.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="TriggerDependentProperties" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Used to control if the Dependent properties feature is enabled.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="EnableIsChangedProperty" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Used to control if the IsChanged property feature is enabled.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="EventInvokerNames" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.</xs:documentation>
|
||||
|
|
@ -31,6 +41,16 @@
|
|||
<xs:documentation>Used to control if equality checks should use the static Equals method resolved from the base class.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="SuppressWarnings" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Used to turn off build warnings from this weaver.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="SuppressOnPropertyNameChangedWarning" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Used to turn off build warnings about mismatched On_PropertyName_Changed methods.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ namespace Dalamud.Game.Addon
|
|||
internal sealed unsafe partial class DalamudSystemMenu
|
||||
{
|
||||
private readonly Dalamud dalamud;
|
||||
private AtkValueChangeType atkValueChangeType;
|
||||
private AtkValueSetString atkValueSetString;
|
||||
private Hook<AgentHudOpenSystemMenuPrototype> hookAgentHudOpenSystemMenu;
|
||||
private readonly AtkValueChangeType atkValueChangeType;
|
||||
private readonly AtkValueSetString atkValueSetString;
|
||||
private readonly Hook<AgentHudOpenSystemMenuPrototype> hookAgentHudOpenSystemMenu;
|
||||
// TODO: Make this into events in Framework.Gui
|
||||
private Hook<UiModuleRequestMainCommand> hookUiModuleRequestMainCommand;
|
||||
private readonly Hook<UiModuleRequestMainCommand> hookUiModuleRequestMainCommand;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DalamudSystemMenu"/> class.
|
||||
|
|
@ -34,13 +34,10 @@ namespace Dalamud.Game.Addon
|
|||
|
||||
this.hookAgentHudOpenSystemMenu = new Hook<AgentHudOpenSystemMenuPrototype>(openSystemMenuAddress, this.AgentHudOpenSystemMenuDetour);
|
||||
|
||||
var atkValueChangeTypeAddress =
|
||||
this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 45 84 F6 48 8D 4C 24 ??");
|
||||
this.atkValueChangeType =
|
||||
Marshal.GetDelegateForFunctionPointer<AtkValueChangeType>(atkValueChangeTypeAddress);
|
||||
var atkValueChangeTypeAddress = this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 45 84 F6 48 8D 4C 24 ??");
|
||||
this.atkValueChangeType = Marshal.GetDelegateForFunctionPointer<AtkValueChangeType>(atkValueChangeTypeAddress);
|
||||
|
||||
var atkValueSetStringAddress =
|
||||
this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 41 03 ED");
|
||||
var atkValueSetStringAddress = this.dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 41 03 ED");
|
||||
this.atkValueSetString = Marshal.GetDelegateForFunctionPointer<AtkValueSetString>(atkValueSetStringAddress);
|
||||
|
||||
var uiModuleRequestMainCommandAddress = this.dalamud.SigScanner.ScanText("40 53 56 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 48 8B 01 8B DA 48 8B F1 FF 90 ?? ?? ?? ??");
|
||||
|
|
@ -140,10 +137,10 @@ namespace Dalamud.Game.Addon
|
|||
switch (commandId)
|
||||
{
|
||||
case 69420:
|
||||
this.dalamud.DalamudUi.TogglePluginInstaller();
|
||||
this.dalamud.DalamudUi.TogglePluginInstallerWindow();
|
||||
break;
|
||||
case 69421:
|
||||
this.dalamud.DalamudUi.ToggleSettings();
|
||||
this.dalamud.DalamudUi.ToggleSettingsWindow();
|
||||
break;
|
||||
default:
|
||||
this.hookUiModuleRequestMainCommand.Original(thisPtr, commandId);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
|
@ -11,7 +10,6 @@ using CheapLoc;
|
|||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Dalamud.Interface;
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Game
|
||||
|
|
@ -21,39 +19,39 @@ namespace Dalamud.Game
|
|||
/// </summary>
|
||||
public class ChatHandlers
|
||||
{
|
||||
private static readonly Dictionary<string, string> UnicodeToDiscordEmojiDict = new()
|
||||
{
|
||||
{ "", "<:ffxive071:585847382210642069>" },
|
||||
{ "", "<:ffxive083:585848592699490329>" },
|
||||
};
|
||||
// private static readonly Dictionary<string, string> UnicodeToDiscordEmojiDict = new()
|
||||
// {
|
||||
// { "", "<:ffxive071:585847382210642069>" },
|
||||
// { "", "<:ffxive083:585848592699490329>" },
|
||||
// };
|
||||
|
||||
private readonly Dictionary<XivChatType, Color> handledChatTypeColors = new()
|
||||
{
|
||||
{ XivChatType.CrossParty, Color.DodgerBlue },
|
||||
{ XivChatType.Party, Color.DodgerBlue },
|
||||
{ XivChatType.FreeCompany, Color.DeepSkyBlue },
|
||||
{ XivChatType.CrossLinkShell1, Color.ForestGreen },
|
||||
{ XivChatType.CrossLinkShell2, Color.ForestGreen },
|
||||
{ XivChatType.CrossLinkShell3, Color.ForestGreen },
|
||||
{ XivChatType.CrossLinkShell4, Color.ForestGreen },
|
||||
{ XivChatType.CrossLinkShell5, Color.ForestGreen },
|
||||
{ XivChatType.CrossLinkShell6, Color.ForestGreen },
|
||||
{ XivChatType.CrossLinkShell7, Color.ForestGreen },
|
||||
{ XivChatType.CrossLinkShell8, Color.ForestGreen },
|
||||
{ XivChatType.Ls1, Color.ForestGreen },
|
||||
{ XivChatType.Ls2, Color.ForestGreen },
|
||||
{ XivChatType.Ls3, Color.ForestGreen },
|
||||
{ XivChatType.Ls4, Color.ForestGreen },
|
||||
{ XivChatType.Ls5, Color.ForestGreen },
|
||||
{ XivChatType.Ls6, Color.ForestGreen },
|
||||
{ XivChatType.Ls7, Color.ForestGreen },
|
||||
{ XivChatType.Ls8, Color.ForestGreen },
|
||||
{ XivChatType.TellIncoming, Color.HotPink },
|
||||
{ XivChatType.PvPTeam, Color.SandyBrown },
|
||||
{ XivChatType.Urgent, Color.DarkViolet },
|
||||
{ XivChatType.NoviceNetwork, Color.SaddleBrown },
|
||||
{ XivChatType.Echo, Color.Gray },
|
||||
};
|
||||
// private readonly Dictionary<XivChatType, Color> handledChatTypeColors = new()
|
||||
// {
|
||||
// { XivChatType.CrossParty, Color.DodgerBlue },
|
||||
// { XivChatType.Party, Color.DodgerBlue },
|
||||
// { XivChatType.FreeCompany, Color.DeepSkyBlue },
|
||||
// { XivChatType.CrossLinkShell1, Color.ForestGreen },
|
||||
// { XivChatType.CrossLinkShell2, Color.ForestGreen },
|
||||
// { XivChatType.CrossLinkShell3, Color.ForestGreen },
|
||||
// { XivChatType.CrossLinkShell4, Color.ForestGreen },
|
||||
// { XivChatType.CrossLinkShell5, Color.ForestGreen },
|
||||
// { XivChatType.CrossLinkShell6, Color.ForestGreen },
|
||||
// { XivChatType.CrossLinkShell7, Color.ForestGreen },
|
||||
// { XivChatType.CrossLinkShell8, Color.ForestGreen },
|
||||
// { XivChatType.Ls1, Color.ForestGreen },
|
||||
// { XivChatType.Ls2, Color.ForestGreen },
|
||||
// { XivChatType.Ls3, Color.ForestGreen },
|
||||
// { XivChatType.Ls4, Color.ForestGreen },
|
||||
// { XivChatType.Ls5, Color.ForestGreen },
|
||||
// { XivChatType.Ls6, Color.ForestGreen },
|
||||
// { XivChatType.Ls7, Color.ForestGreen },
|
||||
// { XivChatType.Ls8, Color.ForestGreen },
|
||||
// { XivChatType.TellIncoming, Color.HotPink },
|
||||
// { XivChatType.PvPTeam, Color.SandyBrown },
|
||||
// { XivChatType.Urgent, Color.DarkViolet },
|
||||
// { XivChatType.NoviceNetwork, Color.SaddleBrown },
|
||||
// { XivChatType.Echo, Color.Gray },
|
||||
// };
|
||||
|
||||
private readonly Regex rmtRegex = new(
|
||||
@"4KGOLD|We have sufficient stock|VPK\.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 +94,14 @@ namespace Dalamud.Game
|
|||
private readonly Regex urlRegex = new(@"(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?", RegexOptions.Compiled);
|
||||
|
||||
private readonly Dalamud dalamud;
|
||||
private DalamudLinkPayload openInstallerWindowLink;
|
||||
private readonly DalamudLinkPayload openInstallerWindowLink;
|
||||
private bool hasSeenLoadingMsg;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ChatHandlers"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dalamud">Dalamud instance.</param>
|
||||
public ChatHandlers(Dalamud dalamud)
|
||||
internal ChatHandlers(Dalamud dalamud)
|
||||
{
|
||||
this.dalamud = dalamud;
|
||||
|
||||
|
|
@ -122,23 +120,20 @@ namespace Dalamud.Game
|
|||
public string LastLink { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert a string to SeString and wrap in italics payloads.
|
||||
/// Convert a TextPayload to SeString and wrap in italics payloads.
|
||||
/// </summary>
|
||||
/// <param name="text">Text to convert.</param>
|
||||
/// <returns>SeString payload of italicized text.</returns>
|
||||
private static SeString MakeItalics(string text)
|
||||
{
|
||||
// TODO: when the code OnCharMessage is switched to SeString, this can be a straight insertion of the
|
||||
// italics payloads only, and be a lot cleaner
|
||||
var italicString = new SeString(new List<Payload>(new Payload[]
|
||||
{
|
||||
EmphasisItalicPayload.ItalicsOn,
|
||||
new TextPayload(text),
|
||||
EmphasisItalicPayload.ItalicsOff,
|
||||
}));
|
||||
public static SeString MakeItalics(string text)
|
||||
=> MakeItalics(new TextPayload(text));
|
||||
|
||||
return italicString;
|
||||
}
|
||||
/// <summary>
|
||||
/// Convert a TextPayload to SeString and wrap in italics payloads.
|
||||
/// </summary>
|
||||
/// <param name="text">Text to convert.</param>
|
||||
/// <returns>SeString payload of italicized text.</returns>
|
||||
public static SeString MakeItalics(TextPayload text)
|
||||
=> new(EmphasisItalicPayload.ItalicsOn, text, EmphasisItalicPayload.ItalicsOff);
|
||||
|
||||
private void OnCheckMessageHandled(XivChatType type, uint senderid, ref SeString sender, ref SeString message, ref bool isHandled)
|
||||
{
|
||||
|
|
@ -243,13 +238,13 @@ namespace Dalamud.Game
|
|||
var assemblyVersion = Assembly.GetAssembly(typeof(ChatHandlers)).GetName().Version.ToString();
|
||||
|
||||
this.dalamud.Framework.Gui.Chat.Print(string.Format(Loc.Localize("DalamudWelcome", "Dalamud vD{0} loaded."), assemblyVersion)
|
||||
+ string.Format(Loc.Localize("PluginsWelcome", " {0} plugin(s) loaded."), this.dalamud.PluginManager.Plugins.Count));
|
||||
+ string.Format(Loc.Localize("PluginsWelcome", " {0} plugin(s) loaded."), this.dalamud.PluginManager.InstalledPlugins.Count));
|
||||
|
||||
if (this.dalamud.Configuration.PrintPluginsWelcomeMsg)
|
||||
{
|
||||
foreach (var plugin in this.dalamud.PluginManager.Plugins.OrderBy(x => x.Plugin.Name))
|
||||
foreach (var plugin in this.dalamud.PluginManager.InstalledPlugins.OrderBy(plugin => plugin.Name))
|
||||
{
|
||||
this.dalamud.Framework.Gui.Chat.Print(string.Format(Loc.Localize("DalamudPluginLoaded", " 》 {0} v{1} loaded."), plugin.Plugin.Name, plugin.Definition.AssemblyVersion));
|
||||
this.dalamud.Framework.Gui.Chat.Print(string.Format(Loc.Localize("DalamudPluginLoaded", " 》 {0} v{1} loaded."), plugin.Name, plugin.Manifest.AssemblyVersion));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -257,20 +252,19 @@ namespace Dalamud.Game
|
|||
{
|
||||
this.dalamud.Framework.Gui.Chat.PrintChat(new XivChatEntry
|
||||
{
|
||||
MessageBytes = Encoding.UTF8.GetBytes(Loc.Localize("DalamudUpdated", "The In-Game addon has been updated or was reinstalled successfully! Please check the discord for a full changelog.")),
|
||||
Message = Loc.Localize("DalamudUpdated", "The In-Game addon has been updated or was reinstalled successfully! Please check the discord for a full changelog."),
|
||||
Type = XivChatType.Notice,
|
||||
});
|
||||
|
||||
if (DalamudChangelogWindow.WarrantsChangelog)
|
||||
#pragma warning disable CS0162 // Unreachable code detected
|
||||
this.dalamud.DalamudUi.OpenChangelog();
|
||||
#pragma warning restore CS0162 // Unreachable code detected
|
||||
if (this.dalamud.DalamudUi.WarrantsChangelog)
|
||||
this.dalamud.DalamudUi.OpenChangelogWindow();
|
||||
|
||||
this.dalamud.Configuration.LastVersion = assemblyVersion;
|
||||
this.dalamud.Configuration.Save();
|
||||
}
|
||||
|
||||
Task.Run(() => this.dalamud.PluginRepository.UpdatePlugins(!this.dalamud.Configuration.AutoUpdatePlugins)).ContinueWith(t =>
|
||||
Task.Run(() => this.dalamud.PluginManager.UpdatePlugins(!this.dalamud.Configuration.AutoUpdatePlugins))
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
if (t.IsFaulted)
|
||||
{
|
||||
|
|
@ -278,19 +272,19 @@ namespace Dalamud.Game
|
|||
}
|
||||
else
|
||||
{
|
||||
var updatedPlugins = t.Result.UpdatedPlugins;
|
||||
var updatedPlugins = t.Result;
|
||||
|
||||
if (updatedPlugins != null && updatedPlugins.Any())
|
||||
{
|
||||
if (this.dalamud.Configuration.AutoUpdatePlugins)
|
||||
{
|
||||
this.dalamud.PluginRepository.PrintUpdatedPlugins(updatedPlugins, Loc.Localize("DalamudPluginAutoUpdate", "Auto-update:"));
|
||||
this.dalamud.PluginManager.PrintUpdatedPlugins(updatedPlugins, Loc.Localize("DalamudPluginAutoUpdate", "Auto-update:"));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.dalamud.Framework.Gui.Chat.PrintChat(new XivChatEntry
|
||||
{
|
||||
MessageBytes = new SeString(new List<Payload>()
|
||||
Message = new SeString(new List<Payload>()
|
||||
{
|
||||
new TextPayload(Loc.Localize("DalamudPluginUpdateRequired", "One or more of your plugins needs to be updated. Please use the /xlplugins command in-game to update them!")),
|
||||
new TextPayload(" ["),
|
||||
|
|
@ -300,7 +294,7 @@ namespace Dalamud.Game
|
|||
RawPayload.LinkTerminator,
|
||||
new UIForegroundPayload(this.dalamud.Data, 0),
|
||||
new TextPayload("]"),
|
||||
}).Encode(),
|
||||
}),
|
||||
Type = XivChatType.Urgent,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Game.ClientState.Actors.Types;
|
||||
using Dalamud.Game.ClientState.Actors.Types.NonPlayer;
|
||||
|
|
@ -14,204 +12,131 @@ namespace Dalamud.Game.ClientState.Actors
|
|||
/// <summary>
|
||||
/// This collection represents the currently spawned FFXIV actors.
|
||||
/// </summary>
|
||||
public sealed partial class ActorTable : IReadOnlyCollection<Actor>, ICollection, IDisposable
|
||||
public sealed partial class ActorTable
|
||||
{
|
||||
private const int ActorTableLength = 424;
|
||||
|
||||
#region ReadProcessMemory Hack
|
||||
private static readonly int ActorMemSize = Marshal.SizeOf(typeof(Structs.Actor));
|
||||
private static readonly IntPtr ActorMem = Marshal.AllocHGlobal(ActorMemSize);
|
||||
private static readonly IntPtr CurrentProcessHandle = new(-1);
|
||||
#endregion
|
||||
|
||||
private Dalamud dalamud;
|
||||
private ClientStateAddressResolver address;
|
||||
private List<Actor> actorsCache;
|
||||
private readonly Dalamud dalamud;
|
||||
private readonly ClientStateAddressResolver address;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorTable"/> class.
|
||||
/// Set up the actor table collection.
|
||||
/// </summary>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
/// <param name="addressResolver">The ClientStateAddressResolver instance.</param>
|
||||
public ActorTable(Dalamud dalamud, ClientStateAddressResolver addressResolver)
|
||||
/// <param name="dalamud">The <see cref="dalamud"/> instance.</param>
|
||||
/// <param name="addressResolver">Client state address resolver.</param>
|
||||
internal ActorTable(Dalamud dalamud, ClientStateAddressResolver addressResolver)
|
||||
{
|
||||
this.address = addressResolver;
|
||||
this.dalamud = dalamud;
|
||||
this.address = addressResolver;
|
||||
|
||||
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>
|
||||
/// Gets the amount of currently spawned actors.
|
||||
/// </summary>
|
||||
public int Length => this.ActorsCache.Count;
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
var count = 0;
|
||||
for (var i = 0; i < ActorTableLength; i++)
|
||||
{
|
||||
var ptr = this.GetActorAddress(i);
|
||||
if (ptr != IntPtr.Zero)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Actor> ActorsCache => this.actorsCache ??= this.GetActorTable();
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an actor at the specified spawn index.
|
||||
/// </summary>
|
||||
/// <param name="index">Spawn index.</param>
|
||||
/// <returns><see cref="Actor" /> at the specified spawn index.</returns>
|
||||
/// <returns>An <see cref="Actor"/> at the specified spawn index.</returns>
|
||||
[CanBeNull]
|
||||
public Actor this[int index] => this.ActorsCache[index];
|
||||
public Actor this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
var address = this.GetActorAddress(index);
|
||||
return this.CreateActorReference(address);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an actor struct from memory and create the appropriate <see cref="ObjectKind"/> type of actor.
|
||||
/// Gets the address of the actor at the specified index of the actor table.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset of the actor in the actor table.</param>
|
||||
/// <returns>An instantiated actor.</returns>
|
||||
internal Actor ReadActorFromMemory(IntPtr offset)
|
||||
/// <param name="index">The index of the actor.</param>
|
||||
/// <returns>The memory address of the actor.</returns>
|
||||
public unsafe IntPtr GetActorAddress(int index)
|
||||
{
|
||||
try
|
||||
{
|
||||
// FIXME: hack workaround for trying to access the player on logout, after the main object has been deleted
|
||||
if (!NativeFunctions.ReadProcessMemory(CurrentProcessHandle, offset, ActorMem, ActorMemSize, out _))
|
||||
{
|
||||
Log.Debug("ActorTable - ReadProcessMemory failed: likely player deletion during logout");
|
||||
return null;
|
||||
}
|
||||
if (index >= ActorTableLength)
|
||||
return IntPtr.Zero;
|
||||
|
||||
var actorStruct = Marshal.PtrToStructure<Structs.Actor>(ActorMem);
|
||||
return *(IntPtr*)(this.address.ActorTable + (8 * index));
|
||||
}
|
||||
|
||||
return actorStruct.ObjectKind switch
|
||||
{
|
||||
ObjectKind.Player => new PlayerCharacter(offset, actorStruct, this.dalamud),
|
||||
ObjectKind.BattleNpc => new BattleNpc(offset, actorStruct, this.dalamud),
|
||||
ObjectKind.EventObj => new EventObj(offset, actorStruct, this.dalamud),
|
||||
ObjectKind.Companion => new Npc(offset, actorStruct, this.dalamud),
|
||||
_ => new Actor(offset, actorStruct, this.dalamud),
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Could not read actor from memory.");
|
||||
/// <summary>
|
||||
/// Create a reference to a FFXIV actor.
|
||||
/// </summary>
|
||||
/// <param name="address">The address of the actor in memory.</param>
|
||||
/// <returns><see cref="Actor"/> object or inheritor containing requested data.</returns>
|
||||
[CanBeNull]
|
||||
public unsafe Actor CreateActorReference(IntPtr address)
|
||||
{
|
||||
if (this.dalamud.ClientState.LocalContentId == 0)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetCache() => this.actorsCache = null;
|
||||
if (address == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
private void Framework_OnUpdateEvent(Internal.Framework framework)
|
||||
{
|
||||
this.ResetCache();
|
||||
}
|
||||
|
||||
private IntPtr[] GetPointerTable()
|
||||
{
|
||||
var ret = new IntPtr[ActorTableLength];
|
||||
Marshal.Copy(this.address.ActorTable, ret, 0, ActorTableLength);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private List<Actor> GetActorTable()
|
||||
{
|
||||
var actors = new List<Actor>();
|
||||
var ptrTable = this.GetPointerTable();
|
||||
for (var i = 0; i < ActorTableLength; i++)
|
||||
var objKind = *(ObjectKind*)(address + ActorOffsets.ObjectKind);
|
||||
return objKind switch
|
||||
{
|
||||
actors.Add(ptrTable[i] != IntPtr.Zero ? this.ReadActorFromMemory(ptrTable[i]) : null);
|
||||
}
|
||||
|
||||
return actors;
|
||||
ObjectKind.Player => new PlayerCharacter(address, this.dalamud),
|
||||
ObjectKind.BattleNpc => new BattleNpc(address, this.dalamud),
|
||||
ObjectKind.EventObj => new EventObj(address, this.dalamud),
|
||||
ObjectKind.Companion => new Npc(address, this.dalamud),
|
||||
_ => new Actor(address, this.dalamud),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementing IDisposable.
|
||||
/// This collection represents the currently spawned FFXIV actors.
|
||||
/// </summary>
|
||||
public sealed partial class ActorTable : IDisposable
|
||||
public sealed partial class ActorTable : IReadOnlyCollection<Actor>, ICollection
|
||||
{
|
||||
private bool disposed = false;
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="ActorTable"/> class.
|
||||
/// </summary>
|
||||
~ActorTable() => this.Dispose(false);
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of managed and unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (this.disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
this.dalamud.Framework.OnUpdateEvent -= this.Framework_OnUpdateEvent;
|
||||
Marshal.FreeHGlobal(ActorMem);
|
||||
}
|
||||
|
||||
this.disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementing IReadOnlyCollection, IEnumerable, and Enumerable.
|
||||
/// </summary>
|
||||
public sealed partial class ActorTable : IReadOnlyCollection<Actor>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the number of elements in the collection.
|
||||
/// </summary>
|
||||
/// <returns>The number of elements in the collection.</returns>
|
||||
/// <inheritdoc/>
|
||||
int IReadOnlyCollection<Actor>.Count => this.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator capable of iterating through the actor table.
|
||||
/// </summary>
|
||||
/// <returns>An actor enumerable.</returns>
|
||||
public IEnumerator<Actor> GetEnumerator() => this.ActorsCache.Where(a => a != null).GetEnumerator();
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator capable of iterating through the actor table.
|
||||
/// </summary>
|
||||
/// <returns>An actor enumerable.</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementing ICollection.
|
||||
/// </summary>
|
||||
public sealed partial class ActorTable : ICollection
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the number of elements in the collection.
|
||||
/// </summary>
|
||||
/// <returns>The number of elements in the collection.</returns>
|
||||
/// <inheritdoc/>
|
||||
int ICollection.Count => this.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether access to the collection is synchronized (thread safe).
|
||||
/// </summary>
|
||||
/// <returns>Whether access is synchronized (thread safe) or not.</returns>
|
||||
/// <inheritdoc/>
|
||||
bool ICollection.IsSynchronized => false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object that can be used to synchronize access to the collection.
|
||||
/// </summary>
|
||||
/// <returns>An object that can be used to synchronize access to the collection.</returns>
|
||||
/// <inheritdoc/>
|
||||
object ICollection.SyncRoot => this;
|
||||
|
||||
/// <summary>
|
||||
/// Copies the elements of the collection to an array, starting at a particular index.
|
||||
/// </summary>
|
||||
/// <param name="array">
|
||||
/// The one-dimensional array that is the destination of the elements copied from the collection. The array must have zero-based indexing.
|
||||
/// </param>
|
||||
/// <param name="index">
|
||||
/// The zero-based index in array at which copying begins.
|
||||
/// </param>
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<Actor> GetEnumerator()
|
||||
{
|
||||
for (var i = 0; i < ActorTableLength; i++)
|
||||
{
|
||||
yield return this[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
|
||||
|
||||
/// <inheritdoc/>
|
||||
void ICollection.CopyTo(Array array, int index)
|
||||
{
|
||||
for (var i = 0; i < this.Length; i++)
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
namespace Dalamud.Game.ClientState.Actors.Resolvers
|
||||
{
|
||||
/// <summary>
|
||||
/// Base object resolver.
|
||||
/// </summary>
|
||||
public abstract class BaseResolver
|
||||
{
|
||||
private Dalamud dalamud;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseResolver"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
public BaseResolver(Dalamud dalamud)
|
||||
{
|
||||
this.dalamud = dalamud;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Dalamud instance.
|
||||
/// </summary>
|
||||
protected Dalamud Dalamud => this.dalamud;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
namespace Dalamud.Game.ClientState.Actors.Resolvers
|
||||
{
|
||||
/// <summary>
|
||||
/// This object represents a class or job.
|
||||
/// </summary>
|
||||
public class ClassJob : BaseResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// ID of the ClassJob.
|
||||
/// </summary>
|
||||
public readonly uint Id;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClassJob"/> class.
|
||||
/// Set up the ClassJob resolver with the provided ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the classJob.</param>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
public ClassJob(byte id, Dalamud dalamud)
|
||||
: base(dalamud)
|
||||
{
|
||||
this.Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets GameData linked to this ClassJob.
|
||||
/// </summary>
|
||||
public Lumina.Excel.GeneratedSheets.ClassJob GameData =>
|
||||
this.Dalamud.Data.GetExcelSheet<Lumina.Excel.GeneratedSheets.ClassJob>().GetRow(this.Id);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
namespace Dalamud.Game.ClientState.Actors.Resolvers
|
||||
{
|
||||
/// <summary>
|
||||
/// This object represents a world a character can reside on.
|
||||
/// </summary>
|
||||
public class World : BaseResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// ID of the world.
|
||||
/// </summary>
|
||||
public readonly uint Id;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="World"/> class.
|
||||
/// Set up the world resolver with the provided ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the world.</param>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
public World(ushort id, Dalamud dalamud)
|
||||
: base(dalamud)
|
||||
{
|
||||
this.Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets GameData linked to this world.
|
||||
/// </summary>
|
||||
public Lumina.Excel.GeneratedSheets.World GameData =>
|
||||
this.Dalamud.Data.GetExcelSheet<Lumina.Excel.GeneratedSheets.World>().GetRow(this.Id);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Game.ClientState.Actors.Types;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Actors
|
||||
{
|
||||
|
|
@ -10,8 +11,8 @@ namespace Dalamud.Game.ClientState.Actors
|
|||
/// </summary>
|
||||
public sealed class Targets
|
||||
{
|
||||
private Dalamud dalamud;
|
||||
private ClientStateAddressResolver address;
|
||||
private readonly Dalamud dalamud;
|
||||
private readonly ClientStateAddressResolver address;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Targets"/> class.
|
||||
|
|
@ -27,26 +28,31 @@ namespace Dalamud.Game.ClientState.Actors
|
|||
/// <summary>
|
||||
/// Gets the current target.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public Actor CurrentTarget => this.GetActorByOffset(TargetOffsets.CurrentTarget);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mouseover target.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public Actor MouseOverTarget => this.GetActorByOffset(TargetOffsets.MouseOverTarget);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the focus target.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public Actor FocusTarget => this.GetActorByOffset(TargetOffsets.FocusTarget);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the previous target.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public Actor PreviousTarget => this.GetActorByOffset(TargetOffsets.PreviousTarget);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soft target.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public Actor SoftTarget => this.GetActorByOffset(TargetOffsets.SoftTarget);
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -91,6 +97,7 @@ namespace Dalamud.Game.ClientState.Actors
|
|||
Marshal.WriteIntPtr(this.address.TargetManager, offset, actorAddress);
|
||||
}
|
||||
|
||||
[CanBeNull]
|
||||
private Actor GetActorByOffset(int offset)
|
||||
{
|
||||
if (this.address.TargetManager == IntPtr.Zero)
|
||||
|
|
@ -100,7 +107,7 @@ namespace Dalamud.Game.ClientState.Actors
|
|||
if (actorAddress == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
return this.dalamud.ClientState.Actors.ReadActorFromMemory(actorAddress);
|
||||
return this.dalamud.ClientState.Actors.CreateActorReference(actorAddress);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,102 +1,165 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Game.ClientState.Structs;
|
||||
using Serilog;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Memory;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Actors.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represents a basic FFXIV actor.
|
||||
/// This class represents a basic actor (GameObject) in FFXIV.
|
||||
/// </summary>
|
||||
public class Actor : IEquatable<Actor>
|
||||
public unsafe partial class Actor : IEquatable<Actor>
|
||||
{
|
||||
private readonly Structs.Actor actorStruct;
|
||||
private readonly Dalamud dalamud;
|
||||
|
||||
private string name;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Actor"/> class.
|
||||
/// This represents a basic FFXIV actor.
|
||||
/// </summary>
|
||||
/// <param name="actorStruct">The memory representation of the base actor.</param>
|
||||
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
|
||||
/// <param name="address">The address of this actor in memory.</param>
|
||||
public Actor(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
|
||||
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
|
||||
internal Actor(IntPtr address, Dalamud dalamud)
|
||||
{
|
||||
this.actorStruct = actorStruct;
|
||||
this.dalamud = dalamud;
|
||||
this.Dalamud = dalamud;
|
||||
this.Address = address;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position of this <see cref="Actor" />.
|
||||
/// Gets the address of the actor in memory.
|
||||
/// </summary>
|
||||
public Position3 Position => this.ActorStruct.Position;
|
||||
public IntPtr Address { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rotation of this <see cref="Actor" />.
|
||||
/// This ranges from -pi to pi radians.
|
||||
/// Gets Dalamud itself.
|
||||
/// </summary>
|
||||
public float Rotation => this.ActorStruct.Rotation;
|
||||
private protected Dalamud Dalamud { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This allows you to <c>if (actor) {...}</c> to check for validity.
|
||||
/// </summary>
|
||||
/// <param name="actor">The actor to check.</param>
|
||||
/// <returns>True or false.</returns>
|
||||
public static implicit operator bool(Actor actor) => IsValid(actor);
|
||||
|
||||
public static bool operator ==(Actor actor1, Actor actor2)
|
||||
{
|
||||
if (actor1 is null || actor2 is null)
|
||||
return Equals(actor1, actor2);
|
||||
|
||||
return actor1.Equals(actor2);
|
||||
}
|
||||
|
||||
public static bool operator !=(Actor actor1, Actor actor2) => !(actor1 == actor2);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this actor is still valid in memory.
|
||||
/// </summary>
|
||||
/// <param name="actor">The actor to check.</param>
|
||||
/// <returns>True or false.</returns>
|
||||
public static bool IsValid(Actor actor)
|
||||
{
|
||||
if (actor == null)
|
||||
return false;
|
||||
|
||||
if (actor.Dalamud.ClientState.LocalContentId == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this actor is still valid in memory.
|
||||
/// </summary>
|
||||
/// <returns>True or false.</returns>
|
||||
public bool IsValid() => IsValid(this);
|
||||
|
||||
/// <inheritdoc/>
|
||||
bool IEquatable<Actor>.Equals(Actor other) => this.ActorId == other?.ActorId;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object obj) => ((IEquatable<Actor>)this).Equals(obj as Actor);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode() => this.ActorId.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class represents a basic actor (GameObject) in FFXIV.
|
||||
/// </summary>
|
||||
public unsafe partial class Actor
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the displayname of this <see cref="Actor" />.
|
||||
/// </summary>
|
||||
public string Name => this.name ??= Util.GetUTF8String(this.actorStruct.Name);
|
||||
public SeString Name => MemoryHelper.ReadSeString(this.Address + ActorOffsets.Name, 32);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actor ID of this <see cref="Actor" />.
|
||||
/// </summary>
|
||||
public int ActorId => this.ActorStruct.ActorId;
|
||||
public uint ActorId => *(uint*)(this.Address + ActorOffsets.ActorId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hitbox radius of this <see cref="Actor" />.
|
||||
/// Gets the data ID for linking to other respective game data.
|
||||
/// </summary>
|
||||
public float HitboxRadius => this.ActorStruct.HitboxRadius;
|
||||
public uint DataId => *(uint*)(this.Address + ActorOffsets.DataId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID of this GameObject's owner.
|
||||
/// </summary>
|
||||
public uint OwnerId => *(uint*)(this.Address + ActorOffsets.OwnerId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entity kind of this <see cref="Actor" />.
|
||||
/// See <see cref="ObjectKind">the ObjectKind enum</see> for possible values.
|
||||
/// </summary>
|
||||
public ObjectKind ObjectKind => this.ActorStruct.ObjectKind;
|
||||
public ObjectKind ObjectKind => *(ObjectKind*)(this.Address + ActorOffsets.ObjectKind);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sub kind of this Actor.
|
||||
/// </summary>
|
||||
public byte SubKind => *(byte*)(this.Address + ActorOffsets.SubKind);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the actor is friendly.
|
||||
/// </summary>
|
||||
public bool IsFriendly => *(int*)(this.Address + ActorOffsets.IsFriendly) > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the X distance from the local player in yalms.
|
||||
/// </summary>
|
||||
public byte YalmDistanceX => this.ActorStruct.YalmDistanceFromPlayerX;
|
||||
public byte YalmDistanceX => *(byte*)(this.Address + ActorOffsets.YalmDistanceFromObjectX);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target status.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is some kind of enum. It may be <see cref="StatusEffect"/>.
|
||||
/// </remarks>
|
||||
public byte TargetStatus => *(byte*)(this.Address + ActorOffsets.TargetStatus);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Y distance from the local player in yalms.
|
||||
/// </summary>
|
||||
public byte YalmDistanceY => this.ActorStruct.YalmDistanceFromPlayerY;
|
||||
public byte YalmDistanceY => *(byte*)(this.Address + ActorOffsets.YalmDistanceFromObjectY);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target of the actor.
|
||||
/// Gets the position of this <see cref="Actor" />.
|
||||
/// </summary>
|
||||
public virtual int TargetActorID => 0;
|
||||
public Position3 Position => *(Position3*)(this.Address + ActorOffsets.Position);
|
||||
|
||||
/// <summary>
|
||||
/// Gets status Effects.
|
||||
/// Gets the rotation of this <see cref="Actor" />.
|
||||
/// This ranges from -pi to pi radians.
|
||||
/// </summary>
|
||||
public StatusEffect[] StatusEffects => this.ActorStruct.UIStatusEffects;
|
||||
public float Rotation => *(float*)(this.Address + ActorOffsets.Rotation);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of this actor in memory.
|
||||
/// Gets the hitbox radius of this <see cref="Actor" />.
|
||||
/// </summary>
|
||||
public readonly IntPtr Address;
|
||||
public float HitboxRadius => *(float*)(this.Address + ActorOffsets.HitboxRadius);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the memory representation of the base actor.
|
||||
/// Gets the current target of the Actor.
|
||||
/// </summary>
|
||||
internal Structs.Actor ActorStruct => this.actorStruct;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Dalamud"/> backing instance.
|
||||
/// </summary>
|
||||
protected Dalamud Dalamud => this.dalamud;
|
||||
|
||||
/// <inheritdoc/>
|
||||
bool IEquatable<Actor>.Equals(Actor other) => this.ActorId == other.ActorId;
|
||||
public virtual uint TargetActorID => 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
61
Dalamud/Game/ClientState/Actors/Types/ActorOffsets.cs
Normal file
61
Dalamud/Game/ClientState/Actors/Types/ActorOffsets.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Actors.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// Memory offsets for the <see cref="Actor"/> type and all that inherit from it.
|
||||
/// </summary>
|
||||
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Document the offset usage instead.")]
|
||||
public static class ActorOffsets
|
||||
{
|
||||
// GameObject(Actor)
|
||||
// GameObject :: Character
|
||||
// GameObject :: Character :: BattleChara
|
||||
// GameObject :: Character :: Companion
|
||||
|
||||
public const int Name = 0x30;
|
||||
public const int ActorId = 0x74;
|
||||
public const int DataId = 0x80;
|
||||
public const int OwnerId = 0x84;
|
||||
public const int ObjectKind = 0x8C;
|
||||
public const int SubKind = 0x8D;
|
||||
public const int IsFriendly = 0x8E;
|
||||
public const int YalmDistanceFromObjectX = 0x90;
|
||||
public const int TargetStatus = 0x91;
|
||||
public const int YalmDistanceFromObjectY = 0x92;
|
||||
public const int Position = 0xA0;
|
||||
public const int Rotation = 0xB0;
|
||||
public const int HitboxRadius = 0xC0;
|
||||
// End GameObject 0x1A0
|
||||
|
||||
public const int CurrentHp = 0x1C4;
|
||||
public const int MaxHp = 0x1C8;
|
||||
public const int CurrentMp = 0x1CC;
|
||||
public const int MaxMp = 0x1D0;
|
||||
public const int CurrentGp = 0x1D4;
|
||||
public const int MaxGp = 0x1D6;
|
||||
public const int CurrentCp = 0x1D8;
|
||||
public const int MaxCp = 0x1DA;
|
||||
public const int ClassJob = 0x1E2;
|
||||
public const int Level = 0x1E3;
|
||||
public const int PlayerCharacterTargetActorId = 0x230;
|
||||
public const int Customize = 0x1898;
|
||||
public const int CompanyTag = 0x18B2;
|
||||
public const int BattleNpcTargetActorId = 0x18D8;
|
||||
public const int NameId = 0x1940;
|
||||
public const int CurrentWorld = 0x195C;
|
||||
public const int HomeWorld = 0x195E;
|
||||
public const int StatusFlags = 0x19A0;
|
||||
// End Character 0x19B0
|
||||
// End Companion 0x19C0
|
||||
|
||||
public const int UIStatusEffects = 0x19F8;
|
||||
public const int IsCasting = 0x1B80;
|
||||
public const int IsCasting2 = 0x1B82;
|
||||
public const int CurrentCastSpellActionId = 0x1B84;
|
||||
public const int CurrentCastTargetActorId = 0x1B90;
|
||||
public const int CurrentCastTime = 0x1BB4;
|
||||
public const int TotalCastTime = 0x1BB8;
|
||||
// End BattleChara 0x2C00
|
||||
}
|
||||
}
|
||||
|
|
@ -1,80 +1,124 @@
|
|||
using System;
|
||||
|
||||
using Dalamud.Game.ClientState.Actors.Resolvers;
|
||||
using Dalamud.Game.ClientState.Resolvers;
|
||||
using Dalamud.Game.ClientState.Structs;
|
||||
using Dalamud.Memory;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Actors.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represents the base for non-static entities.
|
||||
/// </summary>
|
||||
public class Chara : Actor
|
||||
public unsafe class Chara : Actor
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Chara"/> class.
|
||||
/// This represents a non-static entity.
|
||||
/// </summary>
|
||||
/// <param name="actorStruct">The memory representation of the base actor.</param>
|
||||
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
|
||||
/// <param name="address">The address of this actor in memory.</param>
|
||||
protected Chara(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
|
||||
: base(address, actorStruct, dalamud)
|
||||
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
|
||||
internal Chara(IntPtr address, Dalamud dalamud)
|
||||
: base(address, dalamud)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the level of this Chara.
|
||||
/// </summary>
|
||||
public byte Level => this.ActorStruct.Level;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ClassJob of this Chara.
|
||||
/// </summary>
|
||||
public ClassJob ClassJob => new(this.ActorStruct.ClassJob, this.Dalamud);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current HP of this Chara.
|
||||
/// </summary>
|
||||
public int CurrentHp => this.ActorStruct.CurrentHp;
|
||||
public uint CurrentHp => *(uint*)(this.Address + ActorOffsets.CurrentHp);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum HP of this Chara.
|
||||
/// </summary>
|
||||
public int MaxHp => this.ActorStruct.MaxHp;
|
||||
public uint MaxHp => *(uint*)(this.Address + ActorOffsets.MaxHp);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current MP of this Chara.
|
||||
/// </summary>
|
||||
public int CurrentMp => this.ActorStruct.CurrentMp;
|
||||
public uint CurrentMp => *(uint*)(this.Address + ActorOffsets.CurrentMp);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum MP of this Chara.
|
||||
/// </summary>
|
||||
public int MaxMp => this.ActorStruct.MaxMp;
|
||||
public uint MaxMp => *(uint*)(this.Address + ActorOffsets.MaxMp);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current GP of this Chara.
|
||||
/// </summary>
|
||||
public int CurrentGp => this.ActorStruct.CurrentGp;
|
||||
public uint CurrentGp => *(uint*)(this.Address + ActorOffsets.CurrentGp);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum GP of this Chara.
|
||||
/// </summary>
|
||||
public int MaxGp => this.ActorStruct.MaxGp;
|
||||
public uint MaxGp => *(uint*)(this.Address + ActorOffsets.MaxGp);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current CP of this Chara.
|
||||
/// </summary>
|
||||
public int CurrentCp => this.ActorStruct.CurrentCp;
|
||||
public uint CurrentCp => *(uint*)(this.Address + ActorOffsets.CurrentCp);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum CP of this Chara.
|
||||
/// </summary>
|
||||
public int MaxCp => this.ActorStruct.MaxCp;
|
||||
public uint MaxCp => *(uint*)(this.Address + ActorOffsets.MaxCp);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ClassJob of this Chara.
|
||||
/// </summary>
|
||||
public ClassJobResolver ClassJob => new(*(byte*)(this.Address + ActorOffsets.ClassJob), this.Dalamud);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the level of this Chara.
|
||||
/// </summary>
|
||||
public byte Level => *(byte*)(this.Address + ActorOffsets.Level);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a byte array describing the visual appearance of this Chara.
|
||||
/// Indexed by <see cref="CustomizeIndex"/>.
|
||||
/// </summary>
|
||||
public byte[] Customize => this.ActorStruct.Customize;
|
||||
public byte[] Customize => MemoryHelper.Read<byte>(this.Address + ActorOffsets.Customize, 28);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status flags.
|
||||
/// </summary>
|
||||
public StatusFlags StatusFlags => *(StatusFlags*)(this.Address + ActorOffsets.StatusFlags);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current status effects.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This copies every time it is invoked, so make sure to only grab it once.
|
||||
/// </remarks>
|
||||
public StatusEffect[] StatusEffects => MemoryHelper.Read<StatusEffect>(this.Address + ActorOffsets.UIStatusEffects, 30, true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the actor is currently casting.
|
||||
/// </summary>
|
||||
public bool IsCasting => *(int*)(this.Address + ActorOffsets.IsCasting) > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the actor is currently casting (again?).
|
||||
/// </summary>
|
||||
public bool IsCasting2 => *(int*)(this.Address + ActorOffsets.IsCasting2) > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the spell action ID currently being cast by the actor.
|
||||
/// </summary>
|
||||
public uint CurrentCastSpellActionId => *(uint*)(this.Address + ActorOffsets.CurrentCastSpellActionId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actor ID of the target currently being cast at by the actor.
|
||||
/// </summary>
|
||||
public uint CurrentCastTargetActorId => *(uint*)(this.Address + ActorOffsets.CurrentCastTargetActorId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current casting time of the spell being cast by the actor.
|
||||
/// </summary>
|
||||
public float CurrentCastTime => *(float*)(this.Address + ActorOffsets.CurrentCastTime);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total casting time of the spell being cast by the actor.
|
||||
/// </summary>
|
||||
public float TotalCastTime => *(float*)(this.Address + ActorOffsets.TotalCastTime);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Dalamud.Game.ClientState.Actors
|
||||
namespace Dalamud.Game.ClientState.Actors.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// This enum describes the indices of the Customize array.
|
||||
|
|
@ -5,33 +5,27 @@ namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer
|
|||
/// <summary>
|
||||
/// This class represents a battle NPC.
|
||||
/// </summary>
|
||||
public class BattleNpc : Npc
|
||||
public unsafe class BattleNpc : Npc
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BattleNpc"/> class.
|
||||
/// Set up a new BattleNpc with the provided memory representation.
|
||||
/// </summary>
|
||||
/// <param name="actorStruct">The memory representation of the base actor.</param>
|
||||
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
|
||||
/// <param name="address">The address of this actor in memory.</param>
|
||||
public BattleNpc(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
|
||||
: base(address, actorStruct, dalamud)
|
||||
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
|
||||
internal BattleNpc(IntPtr address, Dalamud dalamud)
|
||||
: base(address, dalamud)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the BattleNpc <see cref="BattleNpcSubKind" /> of this BattleNpc.
|
||||
/// </summary>
|
||||
public BattleNpcSubKind BattleNpcKind => (BattleNpcSubKind)this.ActorStruct.SubKind;
|
||||
public BattleNpcSubKind BattleNpcKind => *(BattleNpcSubKind*)(this.Address + ActorOffsets.SubKind);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID of this BattleNpc's owner.
|
||||
/// Gets the target of the Battle NPC.
|
||||
/// </summary>
|
||||
public int OwnerId => this.ActorStruct.OwnerId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets target of the Battle NPC.
|
||||
/// </summary>
|
||||
public override int TargetActorID => this.ActorStruct.BattleNpcTargetActorId;
|
||||
public override uint TargetActorID => *(uint*)(this.Address + ActorOffsets.BattleNpcTargetActorId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,23 +5,22 @@ namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer
|
|||
/// <summary>
|
||||
/// This class represents an EventObj.
|
||||
/// </summary>
|
||||
public class EventObj : Actor
|
||||
public unsafe class EventObj : Actor
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EventObj"/> class.
|
||||
/// This represents an Event Object.
|
||||
/// Set up a new EventObj with the provided memory representation.
|
||||
/// </summary>
|
||||
/// <param name="actorStruct">The memory representation of the base actor.</param>
|
||||
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
|
||||
/// <param name="address">The address of this actor in memory.</param>
|
||||
public EventObj(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
|
||||
: base(address, actorStruct, dalamud)
|
||||
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
|
||||
internal EventObj(IntPtr address, Dalamud dalamud)
|
||||
: base(address, dalamud)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data ID of the NPC linking to their respective game data.
|
||||
/// Gets the event object ID of the linking to their respective game data.
|
||||
/// </summary>
|
||||
public int DataId => this.ActorStruct.DataId;
|
||||
public uint EventObjectId => *(uint*)(this.Address + ActorOffsets.DataId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,28 +5,27 @@ namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer
|
|||
/// <summary>
|
||||
/// This class represents a NPC.
|
||||
/// </summary>
|
||||
public class Npc : Chara
|
||||
public unsafe class Npc : Chara
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Npc"/> class.
|
||||
/// This represents a Non-playable Character.
|
||||
/// Set up a new NPC with the provided memory representation.
|
||||
/// </summary>
|
||||
/// <param name="actorStruct">The memory representation of the base actor.</param>
|
||||
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
|
||||
/// <param name="address">The address of this actor in memory.</param>
|
||||
public Npc(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
|
||||
: base(address, actorStruct, dalamud)
|
||||
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
|
||||
internal Npc(IntPtr address, Dalamud dalamud)
|
||||
: base(address, dalamud)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data ID of the NPC linking to their respective game data.
|
||||
/// Gets the data ID of the NPC linking to their assoicated BNpcBase data.
|
||||
/// </summary>
|
||||
public int DataId => this.ActorStruct.DataId;
|
||||
public uint BaseId => *(uint*)(this.Address + ActorOffsets.DataId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name ID of the NPC linking to their respective game data.
|
||||
/// </summary>
|
||||
public int NameId => this.ActorStruct.NameId;
|
||||
public uint NameId => *(uint*)(this.Address + ActorOffsets.NameId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Dalamud.Game.ClientState.Actors
|
||||
namespace Dalamud.Game.ClientState.Actors.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum describing possible entity kinds.
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Memory;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Actors.Types
|
||||
{
|
||||
|
|
@ -7,26 +8,6 @@ namespace Dalamud.Game.ClientState.Actors.Types
|
|||
/// </summary>
|
||||
public class PartyMember
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the character.
|
||||
/// </summary>
|
||||
public string CharacterName;
|
||||
|
||||
/// <summary>
|
||||
/// Unknown.
|
||||
/// </summary>
|
||||
public long Unknown;
|
||||
|
||||
/// <summary>
|
||||
/// The actor object that corresponds to this party member.
|
||||
/// </summary>
|
||||
public Actor Actor;
|
||||
|
||||
/// <summary>
|
||||
/// The kind or type of actor.
|
||||
/// </summary>
|
||||
public ObjectKind ObjectKind;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PartyMember"/> class.
|
||||
/// </summary>
|
||||
|
|
@ -34,9 +15,10 @@ namespace Dalamud.Game.ClientState.Actors.Types
|
|||
/// <param name="rawData">The interop data struct.</param>
|
||||
public PartyMember(ActorTable table, Structs.PartyMember rawData)
|
||||
{
|
||||
this.CharacterName = Marshal.PtrToStringAnsi(rawData.namePtr);
|
||||
this.CharacterName = MemoryHelper.ReadSeString(rawData.namePtr);
|
||||
this.Unknown = rawData.unknown;
|
||||
this.Actor = null;
|
||||
|
||||
for (var i = 0; i < table.Length; i++)
|
||||
{
|
||||
if (table[i] != null && table[i].ActorId == rawData.actorId)
|
||||
|
|
@ -48,5 +30,25 @@ namespace Dalamud.Game.ClientState.Actors.Types
|
|||
|
||||
this.ObjectKind = rawData.objectKind;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the character.
|
||||
/// </summary>
|
||||
public SeString CharacterName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets something unknown.
|
||||
/// </summary>
|
||||
public long Unknown { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actor object that corresponds to this party member.
|
||||
/// </summary>
|
||||
public Actor Actor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the kind or type of actor.
|
||||
/// </summary>
|
||||
public ObjectKind ObjectKind { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +1,45 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
using Dalamud.Game.ClientState.Actors.Resolvers;
|
||||
using Dalamud.Game.ClientState.Structs;
|
||||
using Dalamud.Game.ClientState.Resolvers;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Memory;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Actors.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represents a player character.
|
||||
/// </summary>
|
||||
public class PlayerCharacter : Chara
|
||||
public unsafe class PlayerCharacter : Chara
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PlayerCharacter"/> class.
|
||||
/// This represents a player character.
|
||||
/// </summary>
|
||||
/// <param name="actorStruct">The memory representation of the base actor.</param>
|
||||
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
|
||||
/// <param name="address">The address of this actor in memory.</param>
|
||||
public PlayerCharacter(IntPtr address, Structs.Actor actorStruct, Dalamud dalamud)
|
||||
: base(address, actorStruct, dalamud)
|
||||
/// <param name="dalamud">A dalamud reference needed to access game data in Resolvers.</param>
|
||||
internal PlayerCharacter(IntPtr address, Dalamud dalamud)
|
||||
: base(address, dalamud)
|
||||
{
|
||||
var companyTagBytes = new byte[5];
|
||||
Marshal.Copy(this.Address + ActorOffsets.CompanyTag, companyTagBytes, 0, companyTagBytes.Length);
|
||||
this.CompanyTag = Encoding.UTF8.GetString(companyTagBytes.TakeWhile(c => c != 0x0).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current <see cref="World">world</see> of the character.
|
||||
/// Gets the current <see cref="WorldResolver">world</see> of the character.
|
||||
/// </summary>
|
||||
public World CurrentWorld => new(this.ActorStruct.CurrentWorld, this.Dalamud);
|
||||
public WorldResolver CurrentWorld => new(*(ushort*)(this.Address + ActorOffsets.CurrentWorld), this.Dalamud);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the home <see cref="World">world</see> of the character.
|
||||
/// Gets the home <see cref="WorldResolver">world</see> of the character.
|
||||
/// </summary>
|
||||
public World HomeWorld => new(this.ActorStruct.HomeWorld, this.Dalamud);
|
||||
public WorldResolver HomeWorld => new(*(ushort*)(this.Address + ActorOffsets.HomeWorld), this.Dalamud);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Free Company tag of this player.
|
||||
/// </summary>
|
||||
public string CompanyTag { get; private set; }
|
||||
public SeString CompanyTag => MemoryHelper.ReadSeString(this.Address + ActorOffsets.CompanyTag, 6);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target of the PlayerCharacter.
|
||||
/// Gets the target actor ID of the PlayerCharacter.
|
||||
/// </summary>
|
||||
public override int TargetActorID => this.ActorStruct.PlayerCharacterTargetActorId;
|
||||
public override uint TargetActorID => *(uint*)(this.Address + ActorOffsets.PlayerCharacterTargetActorId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
56
Dalamud/Game/ClientState/Actors/Types/StatusFlags.cs
Normal file
56
Dalamud/Game/ClientState/Actors/Types/StatusFlags.cs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Actors.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum describing possible status flags.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum StatusFlags : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// No status flags set.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Hostile actor.
|
||||
/// </summary>
|
||||
Hostile = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Actor in combat.
|
||||
/// </summary>
|
||||
InCombat = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Actor weapon is out.
|
||||
/// </summary>
|
||||
WeaponOut = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Actor offhand is out.
|
||||
/// </summary>
|
||||
OffhandOut = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Actor is a party member.
|
||||
/// </summary>
|
||||
PartyMember = 16,
|
||||
|
||||
/// <summary>
|
||||
/// Actor is a alliance member.
|
||||
/// </summary>
|
||||
AllianceMember = 32,
|
||||
|
||||
/// <summary>
|
||||
/// Actor is in friend list.
|
||||
/// </summary>
|
||||
Friend = 64,
|
||||
|
||||
/// <summary>
|
||||
/// Actor is casting.
|
||||
/// </summary>
|
||||
IsCasting = 128,
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ using System.Runtime.InteropServices;
|
|||
|
||||
using Dalamud.Game.ClientState.Actors;
|
||||
using Dalamud.Game.ClientState.Actors.Types;
|
||||
using Dalamud.Game.ClientState.Fates;
|
||||
using Dalamud.Game.Internal;
|
||||
using Dalamud.Hooking;
|
||||
using JetBrains.Annotations;
|
||||
|
|
@ -15,58 +16,8 @@ namespace Dalamud.Game.ClientState
|
|||
/// <summary>
|
||||
/// This class represents the state of the game client at the time of access.
|
||||
/// </summary>
|
||||
public class ClientState : INotifyPropertyChanged, IDisposable
|
||||
public sealed class ClientState : INotifyPropertyChanged, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The table of all present actors.
|
||||
/// </summary>
|
||||
public readonly ActorTable Actors;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the language of the client.
|
||||
/// </summary>
|
||||
public readonly ClientLanguage ClientLanguage;
|
||||
|
||||
/// <summary>
|
||||
/// The current Territory the player resides in.
|
||||
/// </summary>
|
||||
public ushort TerritoryType;
|
||||
|
||||
/// <summary>
|
||||
/// The class facilitating Job Gauge data access.
|
||||
/// </summary>
|
||||
public JobGauges JobGauges;
|
||||
|
||||
/// <summary>
|
||||
/// The class facilitating party list data access.
|
||||
/// </summary>
|
||||
public PartyList PartyList;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to the keypress state of keyboard keys in game.
|
||||
/// </summary>
|
||||
public KeyState KeyState;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to the button state of gamepad buttons in game.
|
||||
/// </summary>
|
||||
public GamepadState GamepadState;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to client conditions/player state. Allows you to check if a player is in a duty, mounted, etc.
|
||||
/// </summary>
|
||||
public Condition Condition;
|
||||
|
||||
/// <summary>
|
||||
/// The class facilitating target data access.
|
||||
/// </summary>
|
||||
public Targets Targets;
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets fired when the current Territory changes.
|
||||
/// </summary>
|
||||
public EventHandler<ushort> TerritoryChanged;
|
||||
|
||||
private readonly Dalamud dalamud;
|
||||
private readonly ClientStateAddressResolver address;
|
||||
private readonly Hook<SetupTerritoryTypeDelegate> setupTerritoryTypeHook;
|
||||
|
|
@ -80,7 +31,7 @@ namespace Dalamud.Game.ClientState
|
|||
/// <param name="dalamud">Dalamud instance.</param>
|
||||
/// <param name="startInfo">StartInfo of the current Dalamud launch.</param>
|
||||
/// <param name="scanner">Sig scanner.</param>
|
||||
public ClientState(Dalamud dalamud, DalamudStartInfo startInfo, SigScanner scanner)
|
||||
internal ClientState(Dalamud dalamud, DalamudStartInfo startInfo, SigScanner scanner)
|
||||
{
|
||||
this.dalamud = dalamud;
|
||||
this.address = new ClientStateAddressResolver();
|
||||
|
|
@ -92,6 +43,8 @@ namespace Dalamud.Game.ClientState
|
|||
|
||||
this.Actors = new ActorTable(dalamud, this.address);
|
||||
|
||||
this.Fates = new FateTable(dalamud, this.address);
|
||||
|
||||
this.PartyList = new PartyList(dalamud, this.address);
|
||||
|
||||
this.JobGauges = new JobGauges(this.address);
|
||||
|
|
@ -104,7 +57,7 @@ namespace Dalamud.Game.ClientState
|
|||
|
||||
this.Targets = new Targets(dalamud, this.address);
|
||||
|
||||
Log.Verbose("SetupTerritoryType address {SetupTerritoryType}", this.address.SetupTerritoryType);
|
||||
Log.Verbose($"SetupTerritoryType address 0x{this.address.SetupTerritoryType.ToInt64():X}");
|
||||
|
||||
this.setupTerritoryTypeHook = new Hook<SetupTerritoryTypeDelegate>(this.address.SetupTerritoryType, this.SetupTerritoryTypeDetour);
|
||||
|
||||
|
|
@ -122,6 +75,11 @@ namespace Dalamud.Game.ClientState
|
|||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
#pragma warning restore
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets fired when the current Territory changes.
|
||||
/// </summary>
|
||||
public event EventHandler<ushort> TerritoryChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event that fires when a character is logging in.
|
||||
/// </summary>
|
||||
|
|
@ -137,22 +95,61 @@ namespace Dalamud.Game.ClientState
|
|||
/// </summary>
|
||||
public event EventHandler<ContentFinderCondition> CfPop;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the table of all present actors.
|
||||
/// </summary>
|
||||
public ActorTable Actors { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the table of all present fates.
|
||||
/// </summary>
|
||||
public FateTable Fates { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the language of the client.
|
||||
/// </summary>
|
||||
public ClientLanguage ClientLanguage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the class facilitating Job Gauge data access.
|
||||
/// </summary>
|
||||
public JobGauges JobGauges { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the class facilitating party list data access.
|
||||
/// </summary>
|
||||
public PartyList PartyList { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets access to the keypress state of keyboard keys in game.
|
||||
/// </summary>
|
||||
public KeyState KeyState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets access to the button state of gamepad buttons in game.
|
||||
/// </summary>
|
||||
public GamepadState GamepadState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets access to client conditions/player state. Allows you to check if a player is in a duty, mounted, etc.
|
||||
/// </summary>
|
||||
public Condition Condition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the class facilitating target data access.
|
||||
/// </summary>
|
||||
public Targets Targets { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current Territory the player resides in.
|
||||
/// </summary>
|
||||
public ushort TerritoryType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local player character, if one is present.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public PlayerCharacter LocalPlayer
|
||||
{
|
||||
get
|
||||
{
|
||||
var actor = this.Actors[0];
|
||||
|
||||
if (actor is PlayerCharacter pc)
|
||||
return pc;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public PlayerCharacter LocalPlayer => this.Actors[0] as PlayerCharacter;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content ID of the local character.
|
||||
|
|
@ -181,7 +178,6 @@ namespace Dalamud.Game.ClientState
|
|||
{
|
||||
this.PartyList.Dispose();
|
||||
this.setupTerritoryTypeHook.Dispose();
|
||||
this.Actors.Dispose();
|
||||
this.GamepadState.Dispose();
|
||||
|
||||
this.dalamud.Framework.OnUpdateEvent -= this.FrameworkOnOnUpdateEvent;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Game.Internal;
|
||||
|
||||
|
|
@ -16,6 +17,14 @@ namespace Dalamud.Game.ClientState
|
|||
/// </summary>
|
||||
public IntPtr ActorTable { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the fate table pointer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a static address to a pointer, not the address of the table itself.
|
||||
/// </remarks>
|
||||
public IntPtr FateTablePtr { get; private set; }
|
||||
|
||||
// public IntPtr ViewportActorTable { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -50,9 +59,6 @@ namespace Dalamud.Game.ClientState
|
|||
/// </summary>
|
||||
public IntPtr SetupTerritoryType { get; private set; }
|
||||
|
||||
// public IntPtr SomeActorTableAccess { get; private set; }
|
||||
// public IntPtr PartyListUpdate { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the method which polls the gamepads for data.
|
||||
/// Called every frame, even when `Enable Gamepad` is off in the settings.
|
||||
|
|
@ -68,14 +74,18 @@ namespace Dalamud.Game.ClientState
|
|||
// We don't need those anymore, but maybe someone else will - let's leave them here for good measure
|
||||
// ViewportActorTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? 85 ED", 0) + 0x148;
|
||||
// SomeActorTableAccess = sig.ScanText("E8 ?? ?? ?? ?? 48 8D 55 A0 48 8D 8E ?? ?? ?? ??");
|
||||
|
||||
this.ActorTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 44 0F B6 83");
|
||||
|
||||
this.FateTablePtr = sig.GetStaticAddressFromSig("48 8B 15 ?? ?? ?? ?? 48 8B F9 44 0F B7 41 ??");
|
||||
|
||||
this.LocalContentId = sig.GetStaticAddressFromSig("48 0F 44 05 ?? ?? ?? ?? 48 39 07");
|
||||
this.JobGaugeData = sig.GetStaticAddressFromSig("E8 ?? ?? ?? ?? FF C6 48 8D 5B 0C", 0xB9) + 0x10;
|
||||
|
||||
this.SetupTerritoryType = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F9 66 89 91 ?? ?? ?? ??");
|
||||
|
||||
// This resolves to a fixed offset only, without the base address added in, so GetStaticAddressFromSig() can't be used
|
||||
// This resolves to a fixed offset only, without the base address added in,
|
||||
// so GetStaticAddressFromSig() can't be used. lea rcx, ds:1DB9F74h[rax*4]
|
||||
this.KeyboardState = sig.ScanText("48 8D 0C 85 ?? ?? ?? ?? 8B 04 31 85 C2 0F 85") + 0x4;
|
||||
|
||||
// PartyListUpdate = sig.ScanText("E8 ?? ?? ?? ?? 49 8B D7 4C 8D 86 ?? ?? ?? ??");
|
||||
|
|
|
|||
178
Dalamud/Game/ClientState/Fates/FateTable.cs
Normal file
178
Dalamud/Game/ClientState/Fates/FateTable.cs
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Dalamud.Game.ClientState.Fates.Types;
|
||||
using JetBrains.Annotations;
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Fates
|
||||
{
|
||||
/// <summary>
|
||||
/// This collection represents the currently available Fate events.
|
||||
/// </summary>
|
||||
public sealed partial class FateTable
|
||||
{
|
||||
// If the pointer at this offset is 0, do not scan the table
|
||||
private const int CheckPtrOffset = 0x80;
|
||||
private const int FirstPtrOffset = 0x90;
|
||||
private const int LastPtrOffset = 0x98;
|
||||
|
||||
private readonly Dalamud dalamud;
|
||||
private readonly ClientStateAddressResolver address;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FateTable"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dalamud">The <see cref="dalamud"/> instance.</param>
|
||||
/// <param name="addressResolver">Client state address resolver.</param>
|
||||
internal FateTable(Dalamud dalamud, ClientStateAddressResolver addressResolver)
|
||||
{
|
||||
this.address = addressResolver;
|
||||
this.dalamud = dalamud;
|
||||
|
||||
Log.Verbose($"Fate table address 0x{this.address.FateTablePtr.ToInt64():X}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of currently active Fates.
|
||||
/// </summary>
|
||||
public unsafe int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
var fateTable = this.FateTableAddress;
|
||||
if (fateTable == IntPtr.Zero)
|
||||
return 0;
|
||||
|
||||
var check = *(long*)(fateTable + CheckPtrOffset);
|
||||
if (check == 0)
|
||||
return 0;
|
||||
|
||||
var start = *(long*)(fateTable + FirstPtrOffset);
|
||||
var end = *(long*)(fateTable + LastPtrOffset);
|
||||
if (start == 0 || end == 0)
|
||||
return 0;
|
||||
|
||||
return (int)((end - start) / 8);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe IntPtr FateTableAddress
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.address.FateTablePtr == IntPtr.Zero)
|
||||
return IntPtr.Zero;
|
||||
|
||||
return *(IntPtr*)this.address.FateTablePtr;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an actor at the specified spawn index.
|
||||
/// </summary>
|
||||
/// <param name="index">Spawn index.</param>
|
||||
/// <returns>A <see cref="Fate"/> at the specified spawn index.</returns>
|
||||
[CanBeNull]
|
||||
public Fate this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
var address = this.GetFateAddress(index);
|
||||
return this[address];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a Fate at the specified address.
|
||||
/// </summary>
|
||||
/// <param name="address">The Fate address.</param>
|
||||
/// <returns>A <see cref="Fate"/> at the specified address.</returns>
|
||||
public Fate this[IntPtr address]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (address == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
return this.CreateFateReference(address);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the Fate at the specified index of the fate table.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the Fate.</param>
|
||||
/// <returns>The memory address of the Fate.</returns>
|
||||
public unsafe IntPtr GetFateAddress(int index)
|
||||
{
|
||||
if (index >= this.Length)
|
||||
return IntPtr.Zero;
|
||||
|
||||
var fateTable = this.FateTableAddress;
|
||||
if (fateTable == IntPtr.Zero)
|
||||
return IntPtr.Zero;
|
||||
|
||||
var firstFate = *(IntPtr*)(fateTable + FirstPtrOffset);
|
||||
return *(IntPtr*)(firstFate + (8 * index));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a reference to a FFXIV actor.
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset of the actor in memory.</param>
|
||||
/// <returns><see cref="Fate"/> object containing requested data.</returns>
|
||||
[CanBeNull]
|
||||
internal unsafe Fate CreateFateReference(IntPtr offset)
|
||||
{
|
||||
if (this.dalamud.ClientState.LocalContentId == 0)
|
||||
return null;
|
||||
|
||||
if (offset == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
return new Fate(offset, this.dalamud);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This collection represents the currently available Fate events.
|
||||
/// </summary>
|
||||
public sealed partial class FateTable : IReadOnlyCollection<Fate>, ICollection
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
int IReadOnlyCollection<Fate>.Count => this.Length;
|
||||
|
||||
/// <inheritdoc/>
|
||||
int ICollection.Count => this.Length;
|
||||
|
||||
/// <inheritdoc/>
|
||||
bool ICollection.IsSynchronized => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
object ICollection.SyncRoot => this;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<Fate> GetEnumerator()
|
||||
{
|
||||
for (var i = 0; i < this.Length; i++)
|
||||
{
|
||||
yield return this[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
|
||||
|
||||
/// <inheritdoc/>
|
||||
void ICollection.CopyTo(Array array, int index)
|
||||
{
|
||||
for (var i = 0; i < this.Length; i++)
|
||||
{
|
||||
array.SetValue(this[i], index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
138
Dalamud/Game/ClientState/Fates/Types/Fate.cs
Normal file
138
Dalamud/Game/ClientState/Fates/Types/Fate.cs
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
using System;
|
||||
|
||||
using Dalamud.Game.ClientState.Resolvers;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Memory;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Fates.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represents an FFXIV Fate.
|
||||
/// </summary>
|
||||
public unsafe partial class Fate : IEquatable<Fate>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Fate"/> class.
|
||||
/// </summary>
|
||||
/// <param name="address">The address of this fate in memory.</param>
|
||||
/// <param name="dalamud">Dalamud instance.</param>
|
||||
internal Fate(IntPtr address, Dalamud dalamud)
|
||||
{
|
||||
this.Address = address;
|
||||
this.Dalamud = dalamud;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of this Fate in memory.
|
||||
/// </summary>
|
||||
public IntPtr Address { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets Dalamud itself.
|
||||
/// </summary>
|
||||
private protected Dalamud Dalamud { get; }
|
||||
|
||||
public static bool operator ==(Fate fate1, Fate fate2)
|
||||
{
|
||||
if (fate1 is null || fate2 is null)
|
||||
return Equals(fate1, fate2);
|
||||
|
||||
return fate1.Equals(fate2);
|
||||
}
|
||||
|
||||
public static bool operator !=(Fate fate1, Fate fate2) => !(fate1 == fate2);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this Fate is still valid in memory.
|
||||
/// </summary>
|
||||
/// <param name="fate">The fate to check.</param>
|
||||
/// <returns>True or false.</returns>
|
||||
public static bool IsValid(Fate fate)
|
||||
{
|
||||
if (fate == null)
|
||||
return false;
|
||||
|
||||
if (fate.Dalamud.ClientState.LocalContentId == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this actor is still valid in memory.
|
||||
/// </summary>
|
||||
/// <returns>True or false.</returns>
|
||||
public bool IsValid() => IsValid(this);
|
||||
|
||||
/// <inheritdoc/>
|
||||
bool IEquatable<Fate>.Equals(Fate other) => this.FateId == other?.FateId;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object obj) => ((IEquatable<Fate>)this).Equals(obj as Fate);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode() => this.FateId.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class represents an FFXIV Fate.
|
||||
/// </summary>
|
||||
public unsafe partial class Fate
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Fate ID of this <see cref="Fate" />.
|
||||
/// </summary>
|
||||
public ushort FateId => *(ushort*)(this.Address + FateOffsets.FateId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets game data linked to this Fate.
|
||||
/// </summary>
|
||||
public Lumina.Excel.GeneratedSheets.Fate GameData => this.Dalamud.Data.GetExcelSheet<Lumina.Excel.GeneratedSheets.Fate>().GetRow(this.FateId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time this <see cref="Fate"/> started.
|
||||
/// </summary>
|
||||
public int StartTimeEpoch => *(int*)(this.Address + FateOffsets.StartTimeEpoch);
|
||||
|
||||
/// <summary>
|
||||
/// Gets how long this <see cref="Fate"/> will run.
|
||||
/// </summary>
|
||||
public short Duration => *(short*)(this.Address + FateOffsets.Duration);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the remaining time in seconds for this <see cref="Fate"/>.
|
||||
/// </summary>
|
||||
public long TimeRemaining => this.StartTimeEpoch + this.Duration - DateTimeOffset.Now.ToUnixTimeSeconds();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the displayname of this <see cref="Fate" />.
|
||||
/// </summary>
|
||||
public SeString Name => MemoryHelper.ReadSeString((Utf8String*)(this.Address + FateOffsets.Name));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state of this <see cref="Fate"/> (Running, Ended, Failed, Preparation, WaitingForEnd).
|
||||
/// </summary>
|
||||
public FateState State => *(FateState*)(this.Address + FateOffsets.State);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the progress amount of this <see cref="Fate"/>.
|
||||
/// </summary>
|
||||
public byte Progress => *(byte*)(this.Address + FateOffsets.Progress);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the level of this <see cref="Fate"/>.
|
||||
/// </summary>
|
||||
public byte Level => *(byte*)(this.Address + FateOffsets.Level);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position of this <see cref="Fate"/>.
|
||||
/// </summary>
|
||||
public Position3 Position => *(Position3*)(this.Address + FateOffsets.Position);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the territory this <see cref="Fate"/> is located in.
|
||||
/// </summary>
|
||||
public TerritoryTypeResolver TerritoryType => new(*(ushort*)(this.Address + FateOffsets.Territory), this.Dalamud);
|
||||
}
|
||||
}
|
||||
21
Dalamud/Game/ClientState/Fates/Types/FateOffsets.cs
Normal file
21
Dalamud/Game/ClientState/Fates/Types/FateOffsets.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Fates.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// Memory offsets for the <see cref="Fate"/> type.
|
||||
/// </summary>
|
||||
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Document the offset usage instead.")]
|
||||
public static class FateOffsets
|
||||
{
|
||||
public const int FateId = 0x18;
|
||||
public const int StartTimeEpoch = 0x20;
|
||||
public const int Duration = 0x28;
|
||||
public const int Name = 0xC0;
|
||||
public const int State = 0x3AC;
|
||||
public const int Progress = 0x3B8;
|
||||
public const int Level = 0x3F9;
|
||||
public const int Position = 0x450;
|
||||
public const int Territory = 0x74E;
|
||||
}
|
||||
}
|
||||
33
Dalamud/Game/ClientState/Fates/Types/FateState.cs
Normal file
33
Dalamud/Game/ClientState/Fates/Types/FateState.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
namespace Dalamud.Game.ClientState.Fates.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// This represents the state of a single Fate.
|
||||
/// </summary>
|
||||
public enum FateState : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The Fate is active.
|
||||
/// </summary>
|
||||
Running = 0x02,
|
||||
|
||||
/// <summary>
|
||||
/// The Fate has ended.
|
||||
/// </summary>
|
||||
Ended = 0x04,
|
||||
|
||||
/// <summary>
|
||||
/// The player failed the Fate.
|
||||
/// </summary>
|
||||
Failed = 0x05,
|
||||
|
||||
/// <summary>
|
||||
/// The Fate is preparing to run.
|
||||
/// </summary>
|
||||
Preparation = 0x07,
|
||||
|
||||
/// <summary>
|
||||
/// The Fate is preparing to end.
|
||||
/// </summary>
|
||||
WaitingForEnd = 0x08,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
|
||||
using Dalamud.Game.ClientState.Structs;
|
||||
using Dalamud.Hooking;
|
||||
|
|
@ -12,7 +12,7 @@ namespace Dalamud.Game.ClientState
|
|||
///
|
||||
/// Will block game's gamepad input if <see cref="ImGuiConfigFlags.NavEnableGamepad"/> is set.
|
||||
/// </summary>
|
||||
public unsafe class GamepadState
|
||||
public unsafe class GamepadState : IDisposable
|
||||
{
|
||||
private readonly Hook<ControllerPoll> gamepadPoll;
|
||||
|
||||
|
|
@ -29,12 +29,8 @@ namespace Dalamud.Game.ClientState
|
|||
/// <param name="resolver">Resolver knowing the pointer to the GamepadPoll function.</param>
|
||||
public GamepadState(ClientStateAddressResolver resolver)
|
||||
{
|
||||
#if DEBUG
|
||||
Log.Verbose("GamepadPoll address {GamepadPoll}", resolver.GamepadPoll);
|
||||
#endif
|
||||
this.gamepadPoll = new Hook<ControllerPoll>(
|
||||
resolver.GamepadPoll,
|
||||
(ControllerPoll)this.GamepadPollDetour);
|
||||
Log.Verbose($"GamepadPoll address 0x{resolver.GamepadPoll.ToInt64():X}");
|
||||
this.gamepadPoll = new Hook<ControllerPoll>(resolver.GamepadPoll, this.GamepadPollDetour);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace Dalamud.Game.ClientState
|
|||
{
|
||||
this.Address = addressResolver;
|
||||
|
||||
Log.Verbose("JobGaugeData address {JobGaugeData}", this.Address.JobGaugeData);
|
||||
Log.Verbose($"JobGaugeData address 0x{this.Address.JobGaugeData.ToInt64():X}");
|
||||
}
|
||||
|
||||
private ClientStateAddressResolver Address { get; }
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ namespace Dalamud.Game.ClientState
|
|||
{
|
||||
this.bufferBase = moduleBaseAddress + Marshal.ReadInt32(addressResolver.KeyboardState);
|
||||
|
||||
Log.Verbose($"Keyboard state buffer address {this.bufferBase}");
|
||||
Log.Verbose($"Keyboard state buffer address 0x{this.bufferBase.ToInt64():X}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ namespace Dalamud.Game.ClientState
|
|||
/// </summary>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
/// <param name="addressResolver">The ClientStateAddressResolver instance.</param>
|
||||
public PartyList(Dalamud dalamud, ClientStateAddressResolver addressResolver)
|
||||
internal PartyList(Dalamud dalamud, ClientStateAddressResolver addressResolver)
|
||||
{
|
||||
this.address = addressResolver;
|
||||
this.dalamud = dalamud;
|
||||
|
|
|
|||
34
Dalamud/Game/ClientState/Resolvers/BaseResolver{T}.cs
Normal file
34
Dalamud/Game/ClientState/Resolvers/BaseResolver{T}.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
using Lumina.Excel;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Resolvers
|
||||
{
|
||||
/// <summary>
|
||||
/// This object represents a class or job.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of Lumina sheet to resolve.</typeparam>
|
||||
public class BaseResolver<T> where T : ExcelRow
|
||||
{
|
||||
private readonly Dalamud dalamud;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseResolver{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the classJob.</param>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
internal BaseResolver(uint id, Dalamud dalamud)
|
||||
{
|
||||
this.dalamud = dalamud;
|
||||
this.Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID to be resolved.
|
||||
/// </summary>
|
||||
public uint Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets GameData linked to this excel row.
|
||||
/// </summary>
|
||||
public T GameData => this.dalamud.Data.GetExcelSheet<T>().GetRow(this.Id);
|
||||
}
|
||||
}
|
||||
19
Dalamud/Game/ClientState/Resolvers/ClassJobResolver.cs
Normal file
19
Dalamud/Game/ClientState/Resolvers/ClassJobResolver.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
namespace Dalamud.Game.ClientState.Resolvers
|
||||
{
|
||||
/// <summary>
|
||||
/// This object represents a class or job.
|
||||
/// </summary>
|
||||
public class ClassJobResolver : BaseResolver<Lumina.Excel.GeneratedSheets.ClassJob>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClassJobResolver"/> class.
|
||||
/// Set up the ClassJob resolver with the provided ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the classJob.</param>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
internal ClassJobResolver(ushort id, Dalamud dalamud)
|
||||
: base(id, dalamud)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Dalamud/Game/ClientState/Resolvers/FateResolver.cs
Normal file
19
Dalamud/Game/ClientState/Resolvers/FateResolver.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
namespace Dalamud.Game.ClientState.Resolvers
|
||||
{
|
||||
/// <summary>
|
||||
/// This object represents a Fate a character can participate in.
|
||||
/// </summary>
|
||||
public class FateResolver : BaseResolver<Lumina.Excel.GeneratedSheets.Fate>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FateResolver"/> class.
|
||||
/// Set up the Fate resolver with the provided ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the Fate.</param>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
internal FateResolver(ushort id, Dalamud dalamud)
|
||||
: base(id, dalamud)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Dalamud/Game/ClientState/Resolvers/TerritoryTypeResolver.cs
Normal file
19
Dalamud/Game/ClientState/Resolvers/TerritoryTypeResolver.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
namespace Dalamud.Game.ClientState.Resolvers
|
||||
{
|
||||
/// <summary>
|
||||
/// This object represents a territory a character can be in.
|
||||
/// </summary>
|
||||
public class TerritoryTypeResolver : BaseResolver<Lumina.Excel.GeneratedSheets.TerritoryType>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TerritoryTypeResolver"/> class.
|
||||
/// Set up the territory type resolver with the provided ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the territory type.</param>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
internal TerritoryTypeResolver(ushort id, Dalamud dalamud)
|
||||
: base(id, dalamud)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Dalamud/Game/ClientState/Resolvers/WorldResolver.cs
Normal file
19
Dalamud/Game/ClientState/Resolvers/WorldResolver.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
namespace Dalamud.Game.ClientState.Resolvers
|
||||
{
|
||||
/// <summary>
|
||||
/// This object represents a world a character can reside on.
|
||||
/// </summary>
|
||||
public class WorldResolver : BaseResolver<Lumina.Excel.GeneratedSheets.World>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WorldResolver"/> class.
|
||||
/// Set up the world resolver with the provided ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the world.</param>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
internal WorldResolver(ushort id, Dalamud dalamud)
|
||||
: base(id, dalamud)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,289 +0,0 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Game.ClientState.Actors;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Structs
|
||||
{
|
||||
/// <summary>
|
||||
/// Native memory representation of an FFXIV actor.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 2)]
|
||||
public struct Actor
|
||||
{
|
||||
/// <summary>
|
||||
/// The actor name.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.Name)]
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
|
||||
public byte[] Name;
|
||||
|
||||
/// <summary>
|
||||
/// The actor's internal id.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.ActorId)]
|
||||
public int ActorId;
|
||||
|
||||
/// <summary>
|
||||
/// The actor's data id.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.DataId)]
|
||||
public int DataId;
|
||||
|
||||
/// <summary>
|
||||
/// The actor's owner id. This is useful for pets, summons, and the like.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.OwnerId)]
|
||||
public int OwnerId;
|
||||
|
||||
/// <summary>
|
||||
/// The type or kind of actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.ObjectKind)]
|
||||
public ObjectKind ObjectKind;
|
||||
|
||||
/// <summary>
|
||||
/// The sub-type or sub-kind of actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.SubKind)]
|
||||
public byte SubKind;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the actor is friendly.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.IsFriendly)]
|
||||
public bool IsFriendly;
|
||||
|
||||
/// <summary>
|
||||
/// The horizontal distance in game units from the player.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.YalmDistanceFromPlayerX)]
|
||||
public byte YalmDistanceFromPlayerX;
|
||||
|
||||
/// <summary>
|
||||
/// The player target status.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is some kind of enum.
|
||||
/// </remarks>
|
||||
[FieldOffset(ActorOffsets.PlayerTargetStatus)]
|
||||
public byte PlayerTargetStatus;
|
||||
|
||||
/// <summary>
|
||||
/// The vertical distance in game units from the player.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.YalmDistanceFromPlayerY)]
|
||||
public byte YalmDistanceFromPlayerY;
|
||||
|
||||
/// <summary>
|
||||
/// The (X,Z,Y) position of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.Position)]
|
||||
public Position3 Position;
|
||||
|
||||
/// <summary>
|
||||
/// The rotation of the actor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The rotation is around the vertical axis (yaw), from -pi to pi radians.
|
||||
/// </remarks>
|
||||
[FieldOffset(ActorOffsets.Rotation)]
|
||||
public float Rotation;
|
||||
|
||||
/// <summary>
|
||||
/// The hitbox radius of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.HitboxRadius)]
|
||||
public float HitboxRadius;
|
||||
|
||||
/// <summary>
|
||||
/// The current HP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentHp)]
|
||||
public int CurrentHp;
|
||||
|
||||
/// <summary>
|
||||
/// The max HP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.MaxHp)]
|
||||
public int MaxHp;
|
||||
|
||||
/// <summary>
|
||||
/// The current MP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentMp)]
|
||||
public int CurrentMp;
|
||||
|
||||
/// <summary>
|
||||
/// The max MP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.MaxMp)]
|
||||
public short MaxMp;
|
||||
|
||||
/// <summary>
|
||||
/// The current GP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentGp)]
|
||||
public short CurrentGp;
|
||||
|
||||
/// <summary>
|
||||
/// The max GP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.MaxGp)]
|
||||
public short MaxGp;
|
||||
|
||||
/// <summary>
|
||||
/// The current CP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentCp)]
|
||||
public short CurrentCp;
|
||||
|
||||
/// <summary>
|
||||
/// The max CP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.MaxCp)]
|
||||
public short MaxCp;
|
||||
|
||||
/// <summary>
|
||||
/// The class-job of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.ClassJob)]
|
||||
public byte ClassJob;
|
||||
|
||||
/// <summary>
|
||||
/// The level of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.Level)]
|
||||
public byte Level;
|
||||
|
||||
/// <summary>
|
||||
/// The (player character) actor ID being targeted by the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.PlayerCharacterTargetActorId)]
|
||||
public int PlayerCharacterTargetActorId;
|
||||
|
||||
/// <summary>
|
||||
/// The customization byte/bitfield of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.Customize)]
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)]
|
||||
public byte[] Customize;
|
||||
|
||||
// Normally pack=2 should work, but ByTVal or Injection breaks this.
|
||||
// [FieldOffset(ActorOffsets.CompanyTag)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] public string CompanyTag;
|
||||
|
||||
/// <summary>
|
||||
/// The (battle npc) actor ID being targeted by the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.BattleNpcTargetActorId)]
|
||||
public int BattleNpcTargetActorId;
|
||||
|
||||
/// <summary>
|
||||
/// The name ID of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.NameId)]
|
||||
public int NameId;
|
||||
|
||||
/// <summary>
|
||||
/// The current world ID of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentWorld)]
|
||||
public ushort CurrentWorld;
|
||||
|
||||
/// <summary>
|
||||
/// The home world ID of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.HomeWorld)]
|
||||
public ushort HomeWorld;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the actor is currently casting.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.IsCasting)]
|
||||
public bool IsCasting;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the actor is currently casting (dup?).
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.IsCasting2)]
|
||||
public bool IsCasting2;
|
||||
|
||||
/// <summary>
|
||||
/// The spell action ID currently being cast by the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentCastSpellActionId)]
|
||||
public uint CurrentCastSpellActionId;
|
||||
|
||||
/// <summary>
|
||||
/// The actor ID of the target currently being cast at by the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentCastTargetActorId)]
|
||||
public uint CurrentCastTargetActorId;
|
||||
|
||||
/// <summary>
|
||||
/// The current casting time of the spell being cast by the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentCastTime)]
|
||||
public float CurrentCastTime;
|
||||
|
||||
/// <summary>
|
||||
/// The total casting time of the spell being cast by the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.TotalCastTime)]
|
||||
public float TotalCastTime;
|
||||
|
||||
/// <summary>
|
||||
/// The array of status effects that the actor is currently affected by.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.UIStatusEffects)]
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
|
||||
public StatusEffect[] UIStatusEffects;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Memory offsets for the <see cref="Actor"/> type.
|
||||
/// </summary>
|
||||
public static class ActorOffsets
|
||||
{
|
||||
// Reference https://github.com/FFXIVAPP/sharlayan-resources/blob/master/structures/5.4/x64.json for more
|
||||
public const int Name = 48; // 0x0030
|
||||
public const int ActorId = 116; // 0x0074
|
||||
// public const int ??? = 120; // 0x0078 NPCID1
|
||||
public const int DataId = 128; // 0x0080 NPCID2
|
||||
public const int OwnerId = 132; // 0x0084
|
||||
public const int ObjectKind = 140; // 0x008C Type
|
||||
public const int SubKind = 141; // 0x008D
|
||||
public const int IsFriendly = 142; // 0x008E
|
||||
public const int YalmDistanceFromPlayerX = 144; // 0x0090
|
||||
public const int PlayerTargetStatus = 145; // 0x0091
|
||||
public const int YalmDistanceFromPlayerY = 146; // 0x0092 Distance
|
||||
public const int Position = 160; // 0x00A0 (X,Z,Y)
|
||||
public const int Rotation = 176; // 0x00B0 Heading
|
||||
public const int HitboxRadius = 192; // 0x00C0
|
||||
public const int CurrentHp = 452; // 0x01C4 HPCurrent
|
||||
public const int MaxHp = 456; // 0x01C8 HPMax
|
||||
public const int CurrentMp = 460; // 0x01CC MPCurrent
|
||||
public const int MaxMp = 464; // 0x01D0 MPMax
|
||||
public const int CurrentGp = 468; // 0x01D4 GPCurrent
|
||||
public const int MaxGp = 470; // 0x01D6 GPMax
|
||||
public const int CurrentCp = 472; // 0x01D8 CPCurrent
|
||||
public const int MaxCp = 474; // 0x01DA CPMax
|
||||
public const int ClassJob = 482; // 0x01E2 Job
|
||||
public const int Level = 483; // 0x01E3 Level
|
||||
public const int PlayerCharacterTargetActorId = 560; // 0x01F0 TargetID
|
||||
|
||||
public const int Customize = 0x1898; // Needs verification
|
||||
public const int CompanyTag = 0x18B2;
|
||||
public const int BattleNpcTargetActorId = 0x18D8; // Needs verification
|
||||
public const int NameId = 0x1940; // Needs verification
|
||||
public const int CurrentWorld = 0x195C;
|
||||
public const int HomeWorld = 0x195E;
|
||||
|
||||
public const int IsCasting = 0x1B80;
|
||||
public const int IsCasting2 = 0x1B82;
|
||||
public const int CurrentCastSpellActionId = 0x1B84;
|
||||
public const int CurrentCastTargetActorId = 0x1B90;
|
||||
public const int CurrentCastTime = 0x1BB4;
|
||||
public const int TotalCastTime = 0x1BB8;
|
||||
public const int UIStatusEffects = 0x19F8;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Structs
|
||||
{
|
||||
|
|
@ -40,25 +40,37 @@ namespace Dalamud.Game.ClientState.Structs
|
|||
/// <summary>
|
||||
/// Raw input, set the whole time while a button is held. See <see cref="GamepadButtons"/> for the mapping.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a bitfield.
|
||||
/// </remarks>
|
||||
[FieldOffset(0x98)]
|
||||
public ushort ButtonsRaw; // bitfield
|
||||
public ushort ButtonsRaw;
|
||||
|
||||
/// <summary>
|
||||
/// Button pressed, set once when the button is pressed. See <see cref="GamepadButtons"/> for the mapping.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a bitfield.
|
||||
/// </remarks>
|
||||
[FieldOffset(0x9C)]
|
||||
public ushort ButtonsPressed; // bitfield
|
||||
public ushort ButtonsPressed;
|
||||
|
||||
/// <summary>
|
||||
/// Button released input, set once right after the button is not hold anymore. See <see cref="GamepadButtons"/> for the mapping.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a bitfield.
|
||||
/// </remarks>
|
||||
[FieldOffset(0xA0)]
|
||||
public ushort ButtonsReleased; // bitfield
|
||||
public ushort ButtonsReleased;
|
||||
|
||||
/// <summary>
|
||||
/// Repeatedly emits the held button input in fixed intervals. See <see cref="GamepadButtons"/> for the mapping.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a bitfield.
|
||||
/// </remarks>
|
||||
[FieldOffset(0xA4)]
|
||||
public ushort ButtonsRepeat; // bitfield
|
||||
public ushort ButtonsRepeat;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,35 +8,43 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct BLMGauge
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the time until the next Polyglot stack in milliseconds.
|
||||
/// </summary>
|
||||
[FieldOffset(0)]
|
||||
public short TimeUntilNextPolyglot; // enochian timer
|
||||
private short timeUntilNextPolyglot; // enochian timer
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time remaining for Astral Fire or Umbral Ice in milliseconds.
|
||||
/// </summary>
|
||||
[FieldOffset(2)]
|
||||
public short ElementTimeRemaining; // umbral ice and astral fire timer
|
||||
private short elementTimeRemaining; // umbral ice and astral fire timer
|
||||
|
||||
[FieldOffset(4)]
|
||||
private byte elementStance; // umbral ice or astral fire
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of Umbral Hearts remaining.
|
||||
/// </summary>
|
||||
[FieldOffset(5)]
|
||||
public byte NumUmbralHearts;
|
||||
private byte numUmbralHearts;
|
||||
|
||||
[FieldOffset(6)]
|
||||
private byte numPolyglotStacks;
|
||||
|
||||
[FieldOffset(7)]
|
||||
private byte enochianState;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time until the next Polyglot stack in milliseconds.
|
||||
/// </summary>
|
||||
public short TimeUntilNextPolyglot => this.timeUntilNextPolyglot;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time remaining for Astral Fire or Umbral Ice in milliseconds.
|
||||
/// </summary>
|
||||
public short ElementTimeRemaining => this.elementTimeRemaining;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of Polyglot stacks remaining.
|
||||
/// </summary>
|
||||
[FieldOffset(6)]
|
||||
public byte NumPolyglotStacks;
|
||||
public byte NumPolyglotStacks => this.numPolyglotStacks;
|
||||
|
||||
[FieldOffset(7)]
|
||||
private byte enochianState;
|
||||
/// <summary>
|
||||
/// Gets the number of Umbral Hearts remaining.
|
||||
/// </summary>
|
||||
public byte NumUmbralHearts => this.numUmbralHearts;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the player is in Umbral Ice.
|
||||
|
|
|
|||
|
|
@ -8,28 +8,36 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct BRDGauge
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
private short songTimer;
|
||||
|
||||
[FieldOffset(2)]
|
||||
private byte numSongStacks;
|
||||
|
||||
[FieldOffset(3)]
|
||||
private byte soulVoiceValue;
|
||||
|
||||
[FieldOffset(4)]
|
||||
private CurrentSong activeSong;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current song timer in milliseconds.
|
||||
/// </summary>
|
||||
[FieldOffset(0)]
|
||||
public short SongTimer;
|
||||
public short SongTimer => this.songTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of stacks for the current song.
|
||||
/// </summary>
|
||||
[FieldOffset(2)]
|
||||
public byte NumSongStacks;
|
||||
public byte NumSongStacks => this.numSongStacks;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of Soul Voice accumulated.
|
||||
/// </summary>
|
||||
[FieldOffset(3)]
|
||||
public byte SoulVoiceValue;
|
||||
public byte SoulVoiceValue => this.soulVoiceValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of song that is active.
|
||||
/// </summary>
|
||||
[FieldOffset(4)]
|
||||
public CurrentSong ActiveSong;
|
||||
public CurrentSong ActiveSong => this.activeSong;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,30 +8,37 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct DNCGauge
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the number of feathers available.
|
||||
/// </summary>
|
||||
[FieldOffset(0)]
|
||||
public byte NumFeathers;
|
||||
private byte numFeathers;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of Espirit available.
|
||||
/// </summary>
|
||||
[FieldOffset(1)]
|
||||
public byte Esprit;
|
||||
private byte esprit;
|
||||
|
||||
[FieldOffset(2)]
|
||||
private fixed byte stepOrder[4];
|
||||
|
||||
[FieldOffset(6)]
|
||||
private byte numCompleteSteps;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of feathers available.
|
||||
/// </summary>
|
||||
public byte NumFeathers => this.numFeathers;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of Espirit available.
|
||||
/// </summary>
|
||||
public byte Esprit => this.esprit;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of steps completed for the current dance.
|
||||
/// </summary>
|
||||
[FieldOffset(6)]
|
||||
public byte NumCompleteSteps;
|
||||
public byte NumCompleteSteps => this.numCompleteSteps;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next step in the current dance.
|
||||
/// </summary>
|
||||
/// <returns>The next dance step action ID.</returns>
|
||||
public ulong NextStep() => (ulong)(15999 + this.stepOrder[this.NumCompleteSteps] - 1);
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -8,22 +8,28 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct DRGGauge
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
private short botdTimer;
|
||||
|
||||
[FieldOffset(2)]
|
||||
private BOTDState botdState;
|
||||
|
||||
[FieldOffset(3)]
|
||||
private byte eyeCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time remaining for Blood of the Dragon in milliseconds.
|
||||
/// </summary>
|
||||
[FieldOffset(0)]
|
||||
public short BOTDTimer;
|
||||
public short BOTDTimer => this.botdTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current state of Blood of the Dragon.
|
||||
/// </summary>
|
||||
[FieldOffset(2)]
|
||||
public BOTDState BOTDState;
|
||||
public BOTDState BOTDState => this.botdState;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count of eyes opened during Blood of the Dragon.
|
||||
/// </summary>
|
||||
[FieldOffset(3)]
|
||||
public byte EyeCount;
|
||||
public byte EyeCount => this.eyeCount;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,26 +8,32 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct DRKGauge
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the amount of blood accumulated.
|
||||
/// </summary>
|
||||
[FieldOffset(0)]
|
||||
public byte Blood;
|
||||
private byte blood;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Darkside time remaining in milliseconds.
|
||||
/// </summary>
|
||||
[FieldOffset(2)]
|
||||
public ushort DarksideTimeRemaining;
|
||||
private ushort darksideTimeRemaining;
|
||||
|
||||
[FieldOffset(4)]
|
||||
private byte darkArtsState;
|
||||
|
||||
[FieldOffset(6)]
|
||||
private ushort shadowTimeRemaining;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of blood accumulated.
|
||||
/// </summary>
|
||||
public byte Blood => this.blood;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Darkside time remaining in milliseconds.
|
||||
/// </summary>
|
||||
public ushort DarksideTimeRemaining => this.darksideTimeRemaining;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Shadow time remaining in milliseconds.
|
||||
/// </summary>
|
||||
[FieldOffset(6)]
|
||||
public ushort ShadowTimeRemaining;
|
||||
public ushort ShadowTimeRemaining => this.shadowTimeRemaining;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the player has Dark Arts or not.
|
||||
|
|
|
|||
|
|
@ -8,22 +8,28 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct GNBGauge
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
private byte numAmmo;
|
||||
|
||||
[FieldOffset(2)]
|
||||
private short maxTimerDuration;
|
||||
|
||||
[FieldOffset(4)]
|
||||
private byte ammoComboStepNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of ammo available.
|
||||
/// </summary>
|
||||
[FieldOffset(0)]
|
||||
public byte NumAmmo;
|
||||
public byte NumAmmo => this.numAmmo;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the max combo time of the Gnashing Fang combo.
|
||||
/// </summary>
|
||||
[FieldOffset(2)]
|
||||
public short MaxTimerDuration;
|
||||
public short MaxTimerDuration => this.maxTimerDuration;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current step of the Gnashing Fang combo.
|
||||
/// </summary>
|
||||
[FieldOffset(4)]
|
||||
public byte AmmoComboStepNumber;
|
||||
public byte AmmoComboStepNumber => this.ammoComboStepNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,38 +8,48 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct MCHGauge
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
private short overheatTimeRemaining;
|
||||
|
||||
[FieldOffset(2)]
|
||||
private short robotTimeRemaining;
|
||||
|
||||
[FieldOffset(4)]
|
||||
private byte heat;
|
||||
|
||||
[FieldOffset(5)]
|
||||
private byte battery;
|
||||
|
||||
[FieldOffset(6)]
|
||||
private byte lastRobotBatteryPower;
|
||||
|
||||
[FieldOffset(7)]
|
||||
private byte timerActive;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time time remaining for Overheat in milliseconds.
|
||||
/// </summary>
|
||||
[FieldOffset(0)]
|
||||
public short OverheatTimeRemaining;
|
||||
public short OverheatTimeRemaining => this.overheatTimeRemaining;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time remaining for the Rook or Queen in milliseconds.
|
||||
/// </summary>
|
||||
[FieldOffset(2)]
|
||||
public short RobotTimeRemaining;
|
||||
public short RobotTimeRemaining => this.robotTimeRemaining;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current Heat level.
|
||||
/// </summary>
|
||||
[FieldOffset(4)]
|
||||
public byte Heat;
|
||||
public byte Heat => this.heat;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current Battery level.
|
||||
/// </summary>
|
||||
[FieldOffset(5)]
|
||||
public byte Battery;
|
||||
public byte Battery => this.battery;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the battery level of the last Robot.
|
||||
/// </summary>
|
||||
[FieldOffset(6)]
|
||||
public byte LastRobotBatteryPower;
|
||||
|
||||
[FieldOffset(7)]
|
||||
private byte timerActive;
|
||||
public byte LastRobotBatteryPower => this.lastRobotBatteryPower;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the player is currently Overheated.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Structs.JobGauge
|
||||
|
|
@ -9,35 +8,12 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct MNKGauge
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
private byte numChakra;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of Chakra available.
|
||||
/// </summary>
|
||||
[FieldOffset(0)]
|
||||
public byte NumChakra;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Greased Lightning timer in milliseconds.
|
||||
/// </summary>
|
||||
[Obsolete("GL has been removed from the game")]
|
||||
[FieldOffset(0)]
|
||||
public byte GLTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of Greased Lightning stacks.
|
||||
/// </summary>
|
||||
[Obsolete("GL has been removed from the game")]
|
||||
[FieldOffset(2)]
|
||||
public byte NumGLStacks;
|
||||
|
||||
[Obsolete("GL has been removed from the game")]
|
||||
[FieldOffset(4)]
|
||||
private byte glTimerFreezeState;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the Greased Lightning timer has been frozen.
|
||||
/// </summary>
|
||||
/// <returns>><c>true</c> or <c>false</c>.</returns>
|
||||
[Obsolete("GL has been removed from the game")]
|
||||
public bool IsGLTimerFroze() => false;
|
||||
public byte NumChakra => this.numChakra;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,30 +9,28 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct NINGauge
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
private int hutonTimeLeft;
|
||||
|
||||
[FieldOffset(4)]
|
||||
private byte ninki;
|
||||
|
||||
[FieldOffset(5)]
|
||||
private byte numHutonManualCasts;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time left on Huton in milliseconds.
|
||||
/// </summary>
|
||||
// TODO: Probably a short, confirm.
|
||||
[FieldOffset(0)]
|
||||
public int HutonTimeLeft;
|
||||
public int HutonTimeLeft => this.hutonTimeLeft;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of Ninki available.
|
||||
/// </summary>
|
||||
[FieldOffset(4)]
|
||||
public byte Ninki;
|
||||
|
||||
/// <summary>
|
||||
/// Obsolete.
|
||||
/// </summary>
|
||||
[Obsolete("Does not appear to be used")]
|
||||
[FieldOffset(4)]
|
||||
public byte TCJMudrasUsed;
|
||||
public byte Ninki => this.ninki;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of times Huton has been cast manually.
|
||||
/// </summary>
|
||||
[FieldOffset(5)]
|
||||
public byte NumHutonManualCasts;
|
||||
public byte NumHutonManualCasts => this.numHutonManualCasts;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct PLDGauge
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
private byte gaugeAmount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current level of the Oath gauge.
|
||||
/// </summary>
|
||||
[FieldOffset(0)]
|
||||
public byte GaugeAmount;
|
||||
public byte GaugeAmount => this.gaugeAmount;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,20 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct RDMGauge
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
private byte whiteGauge;
|
||||
|
||||
[FieldOffset(1)]
|
||||
private byte blackGauge;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the level of the White gauge.
|
||||
/// </summary>
|
||||
[FieldOffset(0)]
|
||||
public byte WhiteGauge;
|
||||
public byte WhiteGauge => this.whiteGauge;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the level of the Black gauge.
|
||||
/// </summary>
|
||||
[FieldOffset(1)]
|
||||
public byte BlackGauge;
|
||||
public byte BlackGauge => this.blackGauge;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,23 +8,29 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct SAMGauge
|
||||
{
|
||||
[FieldOffset(3)]
|
||||
private byte kenki;
|
||||
|
||||
[FieldOffset(4)]
|
||||
private byte meditationStacks;
|
||||
|
||||
[FieldOffset(5)]
|
||||
private Sen sen;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current amount of Kenki available.
|
||||
/// </summary>
|
||||
[FieldOffset(3)]
|
||||
public byte Kenki;
|
||||
public byte Kenki => this.kenki;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of Meditation stacks.
|
||||
/// </summary>
|
||||
[FieldOffset(4)]
|
||||
public byte MeditationStacks;
|
||||
public byte MeditationStacks => this.meditationStacks;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the active Sen.
|
||||
/// </summary>
|
||||
[FieldOffset(5)]
|
||||
public Sen Sen;
|
||||
public Sen Sen => this.sen;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the Setsu Sen is active.
|
||||
|
|
|
|||
|
|
@ -8,28 +8,36 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct SCHGauge
|
||||
{
|
||||
[FieldOffset(2)]
|
||||
private byte numAetherflowStacks;
|
||||
|
||||
[FieldOffset(3)]
|
||||
private byte fairyGaugeAmount;
|
||||
|
||||
[FieldOffset(4)]
|
||||
private short seraphTimer;
|
||||
|
||||
[FieldOffset(6)]
|
||||
private DismissedFairy dismissedFairy;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of Aetherflow stacks available.
|
||||
/// </summary>
|
||||
[FieldOffset(2)]
|
||||
public byte NumAetherflowStacks;
|
||||
public byte NumAetherflowStacks => this.numAetherflowStacks;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current level of the Fairy Gauge.
|
||||
/// </summary>
|
||||
[FieldOffset(3)]
|
||||
public byte FairyGaugeAmount;
|
||||
public byte FairyGaugeAmount => this.fairyGaugeAmount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Seraph time remaining in milliseconds.
|
||||
/// </summary>
|
||||
[FieldOffset(4)]
|
||||
public short SeraphTimer;
|
||||
public short SeraphTimer => this.seraphTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last dismissed fairy.
|
||||
/// </summary>
|
||||
[FieldOffset(6)]
|
||||
public DismissedFairy DismissedFairy;
|
||||
public DismissedFairy DismissedFairy => this.dismissedFairy;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,30 +8,38 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct SMNGauge
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
private short timerRemaining;
|
||||
|
||||
[FieldOffset(2)]
|
||||
private SummonPet returnSummon;
|
||||
|
||||
[FieldOffset(3)]
|
||||
private PetGlam returnSummonGlam;
|
||||
|
||||
[FieldOffset(4)]
|
||||
private byte numStacks;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time remaining for the current summon.
|
||||
/// </summary>
|
||||
[FieldOffset(0)]
|
||||
public short TimerRemaining;
|
||||
public short TimerRemaining => this.timerRemaining;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the summon that will return after the current summon expires.
|
||||
/// </summary>
|
||||
[FieldOffset(2)]
|
||||
public SummonPet ReturnSummon;
|
||||
public SummonPet ReturnSummon => this.returnSummon;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the summon glam for the <see cref="ReturnSummon"/>.
|
||||
/// </summary>
|
||||
[FieldOffset(3)]
|
||||
public PetGlam ReturnSummonGlam;
|
||||
public PetGlam ReturnSummonGlam => this.returnSummonGlam;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current stacks.
|
||||
/// Use the summon accessors instead.
|
||||
/// </summary>
|
||||
[FieldOffset(4)]
|
||||
public byte NumStacks;
|
||||
public byte NumStacks => this.numStacks;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if Phoenix is ready to be summoned.
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct WARGauge
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
private byte beastGaugeAmount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of wrath in the Beast gauge.
|
||||
/// </summary>
|
||||
[FieldOffset(0)]
|
||||
public byte BeastGaugeAmount;
|
||||
public byte BeastGaugeAmount => this.beastGaugeAmount;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,22 +8,28 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct WHMGauge
|
||||
{
|
||||
[FieldOffset(2)]
|
||||
private short lilyTimer;
|
||||
|
||||
[FieldOffset(4)]
|
||||
private byte numLilies;
|
||||
|
||||
[FieldOffset(5)]
|
||||
private byte numBloodLily;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time to next lily in milliseconds.
|
||||
/// </summary>
|
||||
[FieldOffset(2)]
|
||||
public short LilyTimer;
|
||||
public short LilyTimer => this.lilyTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of Lilies.
|
||||
/// </summary>
|
||||
[FieldOffset(4)]
|
||||
public byte NumLilies;
|
||||
public byte NumLilies => this.numLilies;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of times the blood lily has been nourished.
|
||||
/// </summary>
|
||||
[FieldOffset(5)]
|
||||
public byte NumBloodLily;
|
||||
public byte NumBloodLily => this.numBloodLily;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Game.ClientState.Actors;
|
||||
using Dalamud.Game.ClientState.Actors.Types;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Structs
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace Dalamud.Game.Command
|
|||
/// </summary>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
/// <param name="language">The client language requested.</param>
|
||||
public CommandManager(Dalamud dalamud, ClientLanguage language)
|
||||
internal CommandManager(Dalamud dalamud, ClientLanguage language)
|
||||
{
|
||||
this.dalamud = dalamud;
|
||||
|
||||
|
|
@ -128,7 +128,8 @@ namespace Dalamud.Game.Command
|
|||
/// <returns>If adding was successful.</returns>
|
||||
public bool AddHandler(string command, CommandInfo info)
|
||||
{
|
||||
if (info == null) throw new ArgumentNullException(nameof(info), "Command handler is null.");
|
||||
if (info == null)
|
||||
throw new ArgumentNullException(nameof(info), "Command handler is null.");
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
|||
383
Dalamud/Game/GameVersion.cs
Normal file
383
Dalamud/Game/GameVersion.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
80
Dalamud/Game/GameVersionConverter.cs
Normal file
80
Dalamud/Game/GameVersionConverter.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,13 +29,13 @@ namespace Dalamud.Game.Internal
|
|||
this.debugCheckAddress = IntPtr.Zero;
|
||||
}
|
||||
|
||||
Log.Verbose("DebugCheck address {DebugCheckAddress}", this.debugCheckAddress);
|
||||
Log.Verbose($"Debug check address 0x{this.debugCheckAddress.ToInt64():X}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the anti-debugging is enabled.
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; private set; }
|
||||
public bool IsEnabled { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enables the anti-debugging by overwriting code in memory.
|
||||
|
|
@ -45,7 +45,7 @@ namespace Dalamud.Game.Internal
|
|||
this.original = new byte[this.nop.Length];
|
||||
if (this.debugCheckAddress != IntPtr.Zero && !this.IsEnabled)
|
||||
{
|
||||
Log.Information($"Overwriting debug check @ 0x{this.debugCheckAddress.ToInt64():X}");
|
||||
Log.Information($"Overwriting debug check at 0x{this.debugCheckAddress.ToInt64():X}");
|
||||
SafeMemory.ReadBytes(this.debugCheckAddress, this.nop.Length, out this.original);
|
||||
SafeMemory.WriteBytes(this.debugCheckAddress, this.nop);
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ namespace Dalamud.Game.Internal
|
|||
{
|
||||
if (this.debugCheckAddress != IntPtr.Zero && this.original != null)
|
||||
{
|
||||
Log.Information($"Reverting debug check @ 0x{this.debugCheckAddress.ToInt64():X}");
|
||||
Log.Information($"Reverting debug check at 0x{this.debugCheckAddress.ToInt64():X}");
|
||||
SafeMemory.WriteBytes(this.debugCheckAddress, this.original);
|
||||
}
|
||||
else
|
||||
|
|
@ -110,9 +110,11 @@ namespace Dalamud.Game.Internal
|
|||
{
|
||||
// If anti-debug is enabled and is being disposed, odds are either the game is exiting, or Dalamud is being reloaded.
|
||||
// If it is the latter, there's half a chance a debugger is currently attached. There's no real need to disable the
|
||||
// check in either situation anyways.
|
||||
// this.Disable();
|
||||
// check in either situation anyways. However if Dalamud is being reloaded, the sig may fail so may as well undo it.
|
||||
this.Disable();
|
||||
}
|
||||
|
||||
this.disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue