From b934dedcfbf6b0bdf2bb3244eb7f058002175d37 Mon Sep 17 00:00:00 2001 From: Anna Clemens Date: Fri, 23 Oct 2020 17:24:32 -0400 Subject: [PATCH] chore: initial commit --- .editorconfig | 203 ++++++ .gitattributes | 2 + .gitignore | 362 ++++++++++ XIVChat Desktop/App.xaml | 9 + XIVChat Desktop/App.xaml.cs | 15 + XIVChat Desktop/AssemblyInfo.cs | 10 + XIVChat Desktop/MainWindow.xaml | 25 + XIVChat Desktop/MainWindow.xaml.cs | 25 + XIVChat Desktop/XIVChat Desktop.csproj | 10 + XIVChat.sln | 42 ++ XIVChatCommon/KeyExchange.cs | 101 +++ XIVChatCommon/Message.cs | 805 ++++++++++++++++++++++ XIVChatCommon/Properties/AssemblyInfo.cs | 36 + XIVChatCommon/SecretMessage.cs | 56 ++ XIVChatCommon/XIVChatCommon.csproj | 88 +++ XIVChatCommon/XivString.cs | 216 ++++++ XIVChatCommon/app.config | 19 + XIVChatCommon/packages.config | 17 + XIVChatPlugin/Configuration.cs | 29 + XIVChatPlugin/Extensions.cs | 65 ++ XIVChatPlugin/GameFunctions.cs | 230 +++++++ XIVChatPlugin/ILMerge.props | 67 ++ XIVChatPlugin/ILMergeOrder.txt | 4 + XIVChatPlugin/Plugin.cs | 106 +++ XIVChatPlugin/PluginUI.cs | 345 ++++++++++ XIVChatPlugin/Properties/AssemblyInfo.cs | 36 + XIVChatPlugin/Resources/lib/libsodium.dll | Bin 0 -> 303604 bytes XIVChatPlugin/Server.cs | 769 +++++++++++++++++++++ XIVChatPlugin/XIVChatPlugin.csproj | 137 ++++ XIVChatPlugin/XIVChatPlugin.json | 9 + XIVChatPlugin/app.config | 27 + XIVChatPlugin/packages.config | 20 + logo-no-background.svg | 84 +++ logo-no-outer-background.svg | 122 ++++ logo-plain.svg | 83 +++ logo.svg | 121 ++++ 36 files changed, 4295 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 XIVChat Desktop/App.xaml create mode 100644 XIVChat Desktop/App.xaml.cs create mode 100644 XIVChat Desktop/AssemblyInfo.cs create mode 100644 XIVChat Desktop/MainWindow.xaml create mode 100644 XIVChat Desktop/MainWindow.xaml.cs create mode 100644 XIVChat Desktop/XIVChat Desktop.csproj create mode 100644 XIVChat.sln create mode 100644 XIVChatCommon/KeyExchange.cs create mode 100644 XIVChatCommon/Message.cs create mode 100644 XIVChatCommon/Properties/AssemblyInfo.cs create mode 100644 XIVChatCommon/SecretMessage.cs create mode 100644 XIVChatCommon/XIVChatCommon.csproj create mode 100644 XIVChatCommon/XivString.cs create mode 100644 XIVChatCommon/app.config create mode 100644 XIVChatCommon/packages.config create mode 100644 XIVChatPlugin/Configuration.cs create mode 100644 XIVChatPlugin/Extensions.cs create mode 100644 XIVChatPlugin/GameFunctions.cs create mode 100644 XIVChatPlugin/ILMerge.props create mode 100644 XIVChatPlugin/ILMergeOrder.txt create mode 100644 XIVChatPlugin/Plugin.cs create mode 100644 XIVChatPlugin/PluginUI.cs create mode 100644 XIVChatPlugin/Properties/AssemblyInfo.cs create mode 100644 XIVChatPlugin/Resources/lib/libsodium.dll create mode 100644 XIVChatPlugin/Server.cs create mode 100644 XIVChatPlugin/XIVChatPlugin.csproj create mode 100644 XIVChatPlugin/XIVChatPlugin.json create mode 100644 XIVChatPlugin/app.config create mode 100644 XIVChatPlugin/packages.config create mode 100644 logo-no-background.svg create mode 100644 logo-no-outer-background.svg create mode 100644 logo-plain.svg create mode 100644 logo.svg diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..279b7d8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,203 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.{cs,json}] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = lf +insert_final_newline = true + +[*.cs] + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = true:error +dotnet_style_qualification_for_field = true:error +dotnet_style_qualification_for_method = true:error +dotnet_style_qualification_for_property = true:error + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent + +# Expression-level preferences +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion + +# Field preferences +dotnet_style_readonly_field = true:suggestion + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:suggestion + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false:silent +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_prefer_static_local_function = true:suggestion +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent + +# Code-block preferences +csharp_prefer_braces = true:error +csharp_prefer_simple_using_statement = true:suggestion + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = false +csharp_new_line_before_else = false +csharp_new_line_before_finally = false +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = none +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..99b6890 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text eol=lf +*.wav binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ee5385 --- /dev/null +++ b/.gitignore @@ -0,0 +1,362 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd diff --git a/XIVChat Desktop/App.xaml b/XIVChat Desktop/App.xaml new file mode 100644 index 0000000..ceab6dd --- /dev/null +++ b/XIVChat Desktop/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/XIVChat Desktop/App.xaml.cs b/XIVChat Desktop/App.xaml.cs new file mode 100644 index 0000000..e1eeccd --- /dev/null +++ b/XIVChat Desktop/App.xaml.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace XIVChat_Desktop { + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application { + } +} diff --git a/XIVChat Desktop/AssemblyInfo.cs b/XIVChat Desktop/AssemblyInfo.cs new file mode 100644 index 0000000..8b5504e --- /dev/null +++ b/XIVChat Desktop/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/XIVChat Desktop/MainWindow.xaml b/XIVChat Desktop/MainWindow.xaml new file mode 100644 index 0000000..95e683b --- /dev/null +++ b/XIVChat Desktop/MainWindow.xaml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + diff --git a/XIVChat Desktop/MainWindow.xaml.cs b/XIVChat Desktop/MainWindow.xaml.cs new file mode 100644 index 0000000..4333cfc --- /dev/null +++ b/XIVChat Desktop/MainWindow.xaml.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace XIVChat_Desktop { + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window { + public MainWindow() { + InitializeComponent(); + } + } +} diff --git a/XIVChat Desktop/XIVChat Desktop.csproj b/XIVChat Desktop/XIVChat Desktop.csproj new file mode 100644 index 0000000..2c2d56c --- /dev/null +++ b/XIVChat Desktop/XIVChat Desktop.csproj @@ -0,0 +1,10 @@ + + + + WinExe + netcoreapp3.1 + XIVChat_Desktop + true + + + \ No newline at end of file diff --git a/XIVChat.sln b/XIVChat.sln new file mode 100644 index 0000000..50a3c2b --- /dev/null +++ b/XIVChat.sln @@ -0,0 +1,42 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30503.244 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XIVChatPlugin", "XIVChatPlugin\XIVChatPlugin.csproj", "{9AEB2B77-C127-4BA8-B9AE-FB3CA1649FDB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9F0334E6-42D0-4CCE-888B-64DBE39D8407}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XIVChatCommon", "XIVChatCommon\XIVChatCommon.csproj", "{6F426EAB-15D4-48AC-80D2-2D2DF2CE5EE0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XIVChat Desktop", "XIVChat Desktop\XIVChat Desktop.csproj", "{D2773EAE-6B9E-4017-9681-585AAB5A99F4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9AEB2B77-C127-4BA8-B9AE-FB3CA1649FDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AEB2B77-C127-4BA8-B9AE-FB3CA1649FDB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AEB2B77-C127-4BA8-B9AE-FB3CA1649FDB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AEB2B77-C127-4BA8-B9AE-FB3CA1649FDB}.Release|Any CPU.Build.0 = Release|Any CPU + {6F426EAB-15D4-48AC-80D2-2D2DF2CE5EE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F426EAB-15D4-48AC-80D2-2D2DF2CE5EE0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F426EAB-15D4-48AC-80D2-2D2DF2CE5EE0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F426EAB-15D4-48AC-80D2-2D2DF2CE5EE0}.Release|Any CPU.Build.0 = Release|Any CPU + {D2773EAE-6B9E-4017-9681-585AAB5A99F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2773EAE-6B9E-4017-9681-585AAB5A99F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2773EAE-6B9E-4017-9681-585AAB5A99F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2773EAE-6B9E-4017-9681-585AAB5A99F4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5833B759-E940-40DC-8768-B20D1257FF86} + EndGlobalSection +EndGlobal diff --git a/XIVChatCommon/KeyExchange.cs b/XIVChatCommon/KeyExchange.cs new file mode 100644 index 0000000..2a71c93 --- /dev/null +++ b/XIVChatCommon/KeyExchange.cs @@ -0,0 +1,101 @@ +using Sodium; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace XIVChatCommon { + public static class KeyExchange { + public static SessionKeys ClientSessionKeys(KeyPair client, byte[] serverPublic) { + var secret = ScalarMult.Mult(client.PrivateKey, serverPublic); + + var combined = secret + .Concat(client.PublicKey) + .Concat(serverPublic) + .ToArray(); + + var hash = GenericHash.Hash(combined, null, 64); + + byte[] rx = new byte[32]; + byte[] tx = new byte[32]; + + for (int i = 0; i < 32; i++) { + rx[i] = hash[i]; + tx[i] = hash[i + 32]; + } + + return new SessionKeys(rx, tx); + } + + public static SessionKeys ServerSessionKeys(KeyPair server, byte[] clientPublic) { + var secret = ScalarMult.Mult(server.PrivateKey, clientPublic); + + var combined = secret + .Concat(clientPublic) + .Concat(server.PublicKey) + .ToArray(); + + var hash = GenericHash.Hash(combined, null, 64); + + byte[] rx = new byte[32]; + byte[] tx = new byte[32]; + + for (int i = 0; i < 32; i++) { + tx[i] = hash[i]; + rx[i] = hash[i + 32]; + } + + return new SessionKeys(rx, tx); + } + + public async static Task ServerHandshake(KeyPair server, Stream stream) { + // get client public key + byte[] clientPublic = new byte[32]; + await stream.ReadAsync(clientPublic, 0, clientPublic.Length); + + // send our public key + await stream.WriteAsync(server.PublicKey, 0, server.PublicKey.Length); + + // get shared secret and derive keys + var keys = ServerSessionKeys(server, clientPublic); + + return new HandshakeInfo(clientPublic, keys); + } + + public async static Task ClientHandshake(KeyPair client, Stream stream) { + // send our public key + await stream.WriteAsync(client.PublicKey, 0, client.PublicKey.Length); + + // get server public key + byte[] serverPublic = new byte[32]; + await stream.ReadAsync(serverPublic, 0, serverPublic.Length); + + // get shared secret and derive keys + var keys = ClientSessionKeys(client, serverPublic); + + return new HandshakeInfo(serverPublic, keys); + } + } + + public class SessionKeys { + public readonly byte[] rx; + public readonly byte[] tx; + + internal SessionKeys(byte[] rx, byte[] tx) { + this.rx = rx; + this.tx = tx; + } + } + + public class HandshakeInfo { + public byte[] RemotePublicKey { get; private set; } + public SessionKeys Keys { get; private set; } + + internal HandshakeInfo(byte[] remote, SessionKeys keys) { + this.RemotePublicKey = remote; + this.Keys = keys; + } + } +} diff --git a/XIVChatCommon/Message.cs b/XIVChatCommon/Message.cs new file mode 100644 index 0000000..4822559 --- /dev/null +++ b/XIVChatCommon/Message.cs @@ -0,0 +1,805 @@ +using MessagePack; +using MessagePack.Formatters; +using System; +using System.Collections.Generic; + +namespace XIVChatCommon { + [MessagePackObject] + public class ServerMessage : IEncodable { + [MessagePackFormatter(typeof(MillisecondsDateTimeFormatter))] + [Key(0)] + public DateTime Timestamp { get; set; } + [Key(1)] + public ChatType Channel { get; set; } + [Key(2)] + public byte[] Sender { get; set; } + [Key(3)] + public byte[] Content { get; set; } + [Key(4)] + public List Chunks { get; set; } + + [IgnoreMember] + public string ContentText => XivString.GetText(this.Content); + [IgnoreMember] + public string SenderText => XivString.GetText(this.Sender); + + [IgnoreMember] + protected override byte Code => (byte)ServerOperation.Message; + + public static ServerMessage Decode(byte[] bytes) { + return MessagePackSerializer.Deserialize(bytes); + } + + protected override byte[] PayloadEncode() { + return MessagePackSerializer.Serialize(this); + } + } + + [Union(1, typeof(TextChunk))] + [Union(2, typeof(IconChunk))] + [MessagePackObject] + public abstract class Chunk { + } + + [MessagePackObject] + public class TextChunk : Chunk { + [Key(0)] + public uint? FallbackColour { get; set; } + [Key(1)] + public uint? Foreground { get; set; } + [Key(2)] + public uint? Glow { get; set; } + [Key(3)] + public bool Italic { get; set; } + [Key(4)] + public string Content { get; set; } + } + + [MessagePackObject] + public class IconChunk : Chunk { + [Key(0)] + public byte Index; + } + + public class NameFormatting { + public string Before { get; private set; } = string.Empty; + public string After { get; private set; } = string.Empty; + public bool IsPresent { get; private set; } = true; + + public static NameFormatting Empty() { + return new NameFormatting { + IsPresent = false, + }; + } + + public static NameFormatting Of(string before, string after) { + return new NameFormatting { + Before = before, + After = after, + }; + } + + public static NameFormatting Basic() { + return new NameFormatting { + Before = "", + After = ": ", + }; + } + } + + public class ChatCode { + private const ushort CLEAR_7 = ~(~0 << 7); + + private readonly ushort code; + + public ChatType Type => (ChatType)(this.code & CLEAR_7); + public ChatSource Source => this.SourceFrom(11); + public ChatSource Target => this.SourceFrom(7); + private ChatSource SourceFrom(ushort shift) => (ChatSource)(1 << ((this.code >> shift) & 0xF)); + + public ChatCode(ushort code) { + this.code = code; + } + + public NameFormatting NameFormat() { + switch (this.Type) { + case ChatType.Say: + case ChatType.Shout: + case ChatType.Yell: + case ChatType.NpcAnnouncement: + case ChatType.NpcDialogue: + return NameFormatting.Of("", ": "); + case ChatType.TellOutgoing: + return NameFormatting.Of(">> ", ": "); + case ChatType.TellIncoming: + return NameFormatting.Of("", " >> "); + case ChatType.GmTell: + return NameFormatting.Of("[GM]", " >> "); + case ChatType.GmSay: + case ChatType.GmShout: + case ChatType.GmYell: + return NameFormatting.Of("[GM]", ": "); + case ChatType.GmParty: + return NameFormatting.Of("([GM]", ") "); + case ChatType.GmFreeCompany: + return NameFormatting.Of("[FC]<[GM]", "> "); + case ChatType.GmLinkshell1: + return NameFormatting.Of("[1]<[GM]", "> "); + case ChatType.GmLinkshell2: + return NameFormatting.Of("[2]<[GM]", "> "); + case ChatType.GmLinkshell3: + return NameFormatting.Of("[3]<[GM]", "> "); + case ChatType.GmLinkshell4: + return NameFormatting.Of("[4]<[GM]", "> "); + case ChatType.GmLinkshell5: + return NameFormatting.Of("[5]<[GM]", "> "); + case ChatType.GmLinkshell6: + return NameFormatting.Of("[6]<[GM]", "> "); + case ChatType.GmLinkshell7: + return NameFormatting.Of("[7]<[GM]", "> "); + case ChatType.GmLinkshell8: + return NameFormatting.Of("[8]<[GM]", "> "); + case ChatType.GmNoviceNetwork: + return NameFormatting.Of("[NOVICE][GM]", ": "); + case ChatType.Party: + case ChatType.CrossParty: + return NameFormatting.Of("(", ") "); + case ChatType.Alliance: + return NameFormatting.Of("((", ")) "); + case ChatType.PvpTeam: + return NameFormatting.Of("[PVP]<", "> "); + case ChatType.FreeCompany: + return NameFormatting.Of("[FC]<", "> "); + case ChatType.Linkshell1: + return NameFormatting.Of("[1]<", "> "); + case ChatType.Linkshell2: + return NameFormatting.Of("[2]<", "> "); + case ChatType.Linkshell3: + return NameFormatting.Of("[3]<", "> "); + case ChatType.Linkshell4: + return NameFormatting.Of("[4]<", "> "); + case ChatType.Linkshell5: + return NameFormatting.Of("[5]<", "> "); + case ChatType.Linkshell6: + return NameFormatting.Of("[6]<", "> "); + case ChatType.Linkshell7: + return NameFormatting.Of("[7]<", "> "); + case ChatType.Linkshell8: + return NameFormatting.Of("[8]<", "> "); + case ChatType.StandardEmote: + return NameFormatting.Empty(); + case ChatType.CustomEmote: + return NameFormatting.Of("", ""); + case ChatType.CrossLinkshell1: + return NameFormatting.Of("[CWLS1]<", "> "); + case ChatType.CrossLinkshell2: + return NameFormatting.Of("[CWLS2]<", "> "); + case ChatType.CrossLinkshell3: + return NameFormatting.Of("[CWLS3]<", "> "); + case ChatType.CrossLinkshell4: + return NameFormatting.Of("[CWLS4]<", "> "); + case ChatType.CrossLinkshell5: + return NameFormatting.Of("[CWLS5]<", "> "); + case ChatType.CrossLinkshell6: + return NameFormatting.Of("[CWLS6]<", "> "); + case ChatType.CrossLinkshell7: + return NameFormatting.Of("[CWLS7]<", "> "); + case ChatType.CrossLinkshell8: + return NameFormatting.Of("[CWLS8]<", "> "); + case ChatType.NoviceNetwork: + return NameFormatting.Of("[NOVICE]", ": "); + default: + return null; + } + } + + public bool IsBattle() { + switch (this.Type) { + case ChatType.Damage: + case ChatType.Miss: + case ChatType.Action: + case ChatType.Item: + case ChatType.Healing: + case ChatType.GainBuff: + case ChatType.LoseBuff: + case ChatType.GainDebuff: + case ChatType.LoseDebuff: + case ChatType.BattleSystem: + return true; + default: + return false; + } + } + + public uint? DefaultColour() { + switch (this.Type) { + case ChatType.Debug: + return Rgba(204, 204, 204); + case ChatType.Urgent: + return Rgba(255, 127, 127); + case ChatType.Notice: + return Rgba(179, 140, 255); + + case ChatType.Say: + return Rgba(247, 247, 247); + case ChatType.Shout: + return Rgba(255, 166, 102); + case ChatType.TellIncoming: + case ChatType.TellOutgoing: + case ChatType.GmTell: + return Rgba(255, 184, 222); + case ChatType.Party: + case ChatType.CrossParty: + return Rgba(102, 229, 255); + case ChatType.Alliance: + return Rgba(255, 127, 0); + case ChatType.NoviceNetwork: + case ChatType.NoviceNetworkSystem: + return Rgba(212, 255, 125); + case ChatType.Linkshell1: + case ChatType.Linkshell2: + case ChatType.Linkshell3: + case ChatType.Linkshell4: + case ChatType.Linkshell5: + case ChatType.Linkshell6: + case ChatType.Linkshell7: + case ChatType.Linkshell8: + case ChatType.CrossLinkshell1: + case ChatType.CrossLinkshell2: + case ChatType.CrossLinkshell3: + case ChatType.CrossLinkshell4: + case ChatType.CrossLinkshell5: + case ChatType.CrossLinkshell6: + case ChatType.CrossLinkshell7: + case ChatType.CrossLinkshell8: + return Rgba(212, 255, 125); + case ChatType.StandardEmote: + return Rgba(186, 255, 240); + case ChatType.CustomEmote: + return Rgba(186, 255, 240); + case ChatType.Yell: + return Rgba(255, 255, 0); + case ChatType.Echo: + return Rgba(204, 204, 204); + case ChatType.System: + case ChatType.GatheringSystem: + case ChatType.PeriodicRecruitmentNotification: + case ChatType.Orchestrion: + case ChatType.Alarm: + case ChatType.RetainerSale: + case ChatType.Sign: + case ChatType.MessageBook: + return Rgba(204, 204, 204); + case ChatType.NpcAnnouncement: + case ChatType.NpcDialogue: + return Rgba(171, 214, 71); + case ChatType.Error: + return Rgba(255, 74, 74); + case ChatType.FreeCompany: + case ChatType.FreeCompanyAnnouncement: + case ChatType.FreeCompanyLoginLogout: + return Rgba(171, 219, 229); + case ChatType.PvpTeam: + return Rgba(171, 219, 229); + case ChatType.PvpTeamAnnouncement: + case ChatType.PvpTeamLoginLogout: + return Rgba(171, 219, 229); + case ChatType.Action: + case ChatType.Item: + case ChatType.LootNotice: + return Rgba(255, 255, 176); + case ChatType.Progress: + return Rgba(255, 222, 115); + case ChatType.LootRoll: + case ChatType.RandomNumber: + return Rgba(199, 191, 158); + case ChatType.Crafting: + case ChatType.Gathering: + return Rgba(222, 191, 247); + case ChatType.Damage: + return Rgba(255, 125, 125); + case ChatType.Miss: + return Rgba(204, 204, 204); + case ChatType.Healing: + return Rgba(212, 255, 125); + case ChatType.GainBuff: + case ChatType.LoseBuff: + return Rgba(148, 191, 255); + case ChatType.GainDebuff: + case ChatType.LoseDebuff: + return Rgba(255, 138, 196); + case ChatType.BattleSystem: + return Rgba(204, 204, 204); + default: + return null; + } + } + + private static uint Rgba(byte red, byte green, byte blue, byte alpha = 0xFF) => alpha + | (uint)(red << 24) + | (uint)(green << 16) + | (uint)(blue << 8); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32")] + public enum ChatType : ushort { + Debug = 1, + Urgent = 2, + Notice = 3, + Say = 10, + Shout = 11, + TellOutgoing = 12, + TellIncoming = 13, + Party = 14, + Alliance = 15, + Linkshell1 = 16, + Linkshell2 = 17, + Linkshell3 = 18, + Linkshell4 = 19, + Linkshell5 = 20, + Linkshell6 = 21, + Linkshell7 = 22, + Linkshell8 = 23, + FreeCompany = 24, + NoviceNetwork = 27, + CustomEmote = 28, + StandardEmote = 29, + Yell = 30, + // 31 - also party? + CrossParty = 32, + PvpTeam = 36, + CrossLinkshell1 = 37, + Damage = 41, + Miss = 42, + Action = 43, + Item = 44, + Healing = 45, + GainBuff = 46, + GainDebuff = 47, + LoseBuff = 48, + LoseDebuff = 49, + Alarm = 55, + Echo = 56, + System = 57, + BattleSystem = 58, + GatheringSystem = 59, + Error = 60, + NpcDialogue = 61, + LootNotice = 62, + Progress = 64, + LootRoll = 65, + Crafting = 66, + Gathering = 67, + NpcAnnouncement = 68, + FreeCompanyAnnouncement = 69, + FreeCompanyLoginLogout = 70, + RetainerSale = 71, + PeriodicRecruitmentNotification = 72, + Sign = 73, + RandomNumber = 74, + NoviceNetworkSystem = 75, + Orchestrion = 76, + PvpTeamAnnouncement = 77, + PvpTeamLoginLogout = 78, + MessageBook = 79, + GmTell = 80, + GmSay = 81, + GmShout = 82, + GmYell = 83, + GmParty = 84, + GmFreeCompany = 85, + GmLinkshell1 = 86, + GmLinkshell2 = 87, + GmLinkshell3 = 88, + GmLinkshell4 = 89, + GmLinkshell5 = 90, + GmLinkshell6 = 91, + GmLinkshell7 = 92, + GmLinkshell8 = 93, + GmNoviceNetwork = 94, + CrossLinkshell2 = 101, + CrossLinkshell3 = 102, + CrossLinkshell4 = 103, + CrossLinkshell5 = 104, + CrossLinkshell6 = 105, + CrossLinkshell7 = 106, + CrossLinkshell8 = 107, + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32")] + public enum ChatSource : ushort { + Self = 2, + PartyMember = 4, + AllianceMember = 8, + Other = 16, + EngagedEnemy = 32, + UnengagedEnemy = 64, + FriendlyNpc = 128, + SelfPet = 256, + PartyPet = 512, + AlliancePet = 1024, + OtherPet = 2048, + } + + [MessagePackObject] + public class ClientMessage : IEncodable { + [Key(0)] + public string Content { get; set; } + + [IgnoreMember] + protected override byte Code => (byte)ClientOperation.Message; + + public static ClientMessage Decode(byte[] bytes) { + return MessagePackSerializer.Deserialize(bytes); + } + + protected override byte[] PayloadEncode() { + return MessagePackSerializer.Serialize(this); + } + } + + public enum ServerOperation : byte { + /// + /// Sent in response to a client ping. Has no payload. + /// + Pong = 1, + /// + /// A message was sent in game and is being relayed to the client. + /// + Message = 2, + /// + /// The server is shutting down. Clients should send no response and close their sockets. Has no payload. + /// + Shutdown = 3, + PlayerData = 4, + Availability = 5, + Channel = 6, + Backlog = 7, + PlayerList = 8, + LinkshellList = 9, + } + + [MessagePackObject] + public class PlayerData : IEncodable { + [Key(0)] + public readonly string homeWorld; + [Key(1)] + public readonly string currentWorld; + [Key(2)] + public readonly string location; + [Key(3)] + public readonly string name; + + public PlayerData(string homeWorld, string currentWorld, string location, string name) { + this.homeWorld = homeWorld; + this.currentWorld = currentWorld; + this.location = location; + this.name = name; + } + + [IgnoreMember] + protected override byte Code => (byte)ServerOperation.PlayerData; + + public static PlayerData Decode(byte[] bytes) { + return MessagePackSerializer.Deserialize(bytes); + } + + protected override byte[] PayloadEncode() { + return MessagePackSerializer.Serialize(this); + } + } + + public class EmptyPlayerData : IEncodable { + public static EmptyPlayerData Instance { get; } = new EmptyPlayerData(); + + [IgnoreMember] + protected override byte Code => (byte)ServerOperation.PlayerData; + + protected override byte[] PayloadEncode() { + return new byte[0]; + } + } + + [MessagePackObject] + public class Availability : IEncodable { + [Key(0)] + public readonly bool available; + + public Availability(bool available) { + this.available = available; + } + + [IgnoreMember] + protected override byte Code => (byte)ServerOperation.Availability; + + public static Availability Decode(byte[] bytes) { + return MessagePackSerializer.Deserialize(bytes); + } + + protected override byte[] PayloadEncode() { + return MessagePackSerializer.Serialize(this); + } + } + + [MessagePackObject] + public class ServerChannel : IEncodable { + [Key(0)] + public readonly byte channel; + [Key(1)] + public readonly string name; + + [IgnoreMember] + public InputChannel InputChannel => (InputChannel)this.channel; + + protected override byte Code => (byte)ServerOperation.Channel; + + public ServerChannel(InputChannel channel, string name) : this((byte)channel, name) { } + + public ServerChannel(byte channel, string name) { + this.channel = channel; + this.name = name; + } + + public static ServerChannel Decode(byte[] bytes) { + return MessagePackSerializer.Deserialize(bytes); + } + + protected override byte[] PayloadEncode() { + return MessagePackSerializer.Serialize(this); + } + } + + public enum InputChannel : byte { + Tell = 0, + Say = 1, + Party = 2, + Alliance = 3, + Yell = 4, + Shout = 5, + FreeCompany = 6, + PvpTeam = 7, + NoviceNetwork = 8, + CrossLinkshell1 = 9, + CrossLinkshell2 = 10, + CrossLinkshell3 = 11, + CrossLinkshell4 = 12, + CrossLinkshell5 = 13, + CrossLinkshell6 = 14, + CrossLinkshell7 = 15, + CrossLinkshell8 = 16, + // 17 - unused? + // 18 - unused? + Linkshell1 = 19, + Linkshell2 = 20, + Linkshell3 = 21, + Linkshell4 = 22, + Linkshell5 = 23, + Linkshell6 = 24, + Linkshell7 = 25, + Linkshell8 = 26, + } + + public class Pong : IEncodable { + public static Pong Instance { get; } = new Pong(); + + [IgnoreMember] + protected override byte Code => (byte)ServerOperation.Pong; + + protected override byte[] PayloadEncode() { + return new byte[0]; + } + } + + public class Ping : IEncodable { + public static Ping Instance { get; } = new Ping(); + + [IgnoreMember] + protected override byte Code => (byte)ClientOperation.Ping; + + protected override byte[] PayloadEncode() { + return new byte[0]; + } + } + + public class ServerShutdown : IEncodable { + public static ServerShutdown Instance { get; } = new ServerShutdown(); + + [IgnoreMember] + protected override byte Code => (byte)ServerOperation.Shutdown; + + protected override byte[] PayloadEncode() { + return new byte[0]; + } + } + + public class ClientShutdown : IEncodable { + public static ClientShutdown Instance { get; } = new ClientShutdown(); + + [IgnoreMember] + protected override byte Code => (byte)ClientOperation.Shutdown; + + protected override byte[] PayloadEncode() { + return new byte[0]; + } + } + + [MessagePackObject] + public class ServerBacklog : IEncodable { + [Key(0)] + public readonly ServerMessage[] messages; + + protected override byte Code => (byte)ServerOperation.Backlog; + + public ServerBacklog(ServerMessage[] messages) { + this.messages = messages; + } + + public static ServerBacklog Decode(byte[] bytes) { + return MessagePackSerializer.Deserialize(bytes); + } + + protected override byte[] PayloadEncode() { + return MessagePackSerializer.Serialize(this); + } + } + + [MessagePackObject] + public class ClientBacklog : IEncodable { + [Key(0)] + public ushort Amount { get; set; } + + protected override byte Code => (byte)ClientOperation.Backlog; + + public static ClientBacklog Decode(byte[] bytes) { + return MessagePackSerializer.Deserialize(bytes); + } + + protected override byte[] PayloadEncode() { + return MessagePackSerializer.Serialize(this); + } + } + + [MessagePackObject] + public class ClientCatchUp : IEncodable { + [MessagePackFormatter(typeof(MillisecondsDateTimeFormatter))] + [Key(0)] + public DateTime After { get; set; } + + protected override byte Code => (byte)ClientOperation.CatchUp; + + public static ClientCatchUp Decode(byte[] bytes) { + return MessagePackSerializer.Deserialize(bytes); + } + + protected override byte[] PayloadEncode() { + return MessagePackSerializer.Serialize(this); + } + } + + [MessagePackObject] + public class ServerPlayerList : IEncodable { + [Key(0)] + public PlayerListType Type { get; set; } + + [Key(1)] + public Player[] Players { get; set; } + + protected override byte Code => (byte)ServerOperation.PlayerList; + + public static ServerPlayerList Decode(byte[] bytes) { + return MessagePackSerializer.Deserialize(bytes); + } + + protected override byte[] PayloadEncode() { + return MessagePackSerializer.Serialize(this); + } + } + + [MessagePackObject] + public class ClientPlayerList : IEncodable { + [Key(0)] + public PlayerListType Type { get; set; } + + protected override byte Code => (byte)ClientOperation.PlayerList; + + public static ClientPlayerList Decode(byte[] bytes) { + return MessagePackSerializer.Deserialize(bytes); + } + + protected override byte[] PayloadEncode() { + return MessagePackSerializer.Serialize(this); + } + } + + public enum PlayerListType : byte { + Party = 1, + Friend = 2, + Linkshell = 3, + CrossLinkshell = 4, + } + + [MessagePackObject] + public class Player { + [Key(0)] + public string Name { get; set; } + [Key(1)] + public string FreeCompany { get; set; } + [Key(2)] + public ulong Status { get; set; } + [Key(3)] + public ushort CurrentWorld { get; set; } + [Key(4)] + public string CurrentWorldName { get; set; } + [Key(5)] + public ushort HomeWorld { get; set; } + [Key(6)] + public string HomeWorldName { get; set; } + [Key(7)] + public ushort Territory { get; set; } + [Key(8)] + public string TerritoryName { get; set; } + [Key(9)] + public byte Job { get; set; } + [Key(10)] + public string JobName { get; set; } + [Key(11)] + public byte GrandCompany { get; set; } + [Key(12)] + public string GrandCompanyName { get; set; } + [Key(13)] + public byte Languages { get; set; } + [Key(14)] + public byte MainLanguage { get; set; } + } + + public abstract class IEncodable { + protected abstract byte Code { get; } + protected abstract byte[] PayloadEncode(); + + public byte[] Encode() { + byte[] payload = this.PayloadEncode(); + + if (payload.Length == 0) { + return new byte[] { this.Code }; + } + + byte[] bytes = new byte[1 + payload.Length]; + bytes[0] = this.Code; + Array.Copy(payload, 0, bytes, 1, payload.Length); + return bytes; + } + } + + public enum ClientOperation : byte { + /// + /// The client is sending data to the server to keep the socket alive. Has no payload. + /// + Ping = 1, + /// + /// The client has a message to be sent in the game and is relaying it to the server. + /// + Message = 2, + /// + /// The client is shutting down. Clients should send this and close their socket for a clean shutdown. + /// + Shutdown = 3, + Backlog = 4, + CatchUp = 5, + PlayerList = 6, + LinkshellList = 7, + } + + public class MillisecondsDateTimeFormatter : IMessagePackFormatter { + private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + + public DateTime Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) { + var millis = reader.ReadInt64(); + return Epoch.AddMilliseconds(millis); + } + + public void Serialize(ref MessagePackWriter writer, DateTime value, MessagePackSerializerOptions options) { + var millis = (long)(value.ToUniversalTime() - Epoch).TotalMilliseconds; + writer.WriteInt64(millis); + } + } +} diff --git a/XIVChatCommon/Properties/AssemblyInfo.cs b/XIVChatCommon/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8cb897c --- /dev/null +++ b/XIVChatCommon/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("XIVChatCommon")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("XIVChatCommon")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6f426eab-15d4-48ac-80d2-2d2df2ce5ee0")] + +// 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")] diff --git a/XIVChatCommon/SecretMessage.cs b/XIVChatCommon/SecretMessage.cs new file mode 100644 index 0000000..2150633 --- /dev/null +++ b/XIVChatCommon/SecretMessage.cs @@ -0,0 +1,56 @@ +using Sodium; +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace XIVChatCommon { + public static class SecretMessage { + private const uint MAX_MESSAGE_LEN = 128_000; + + public async static Task ReadSecretMessage(Stream s, byte[] key, CancellationToken token = default) { + int read = 0; + + byte[] header = new byte[4 + 24]; + while (read < header.Length) { + read += await s.ReadAsync(header, read, header.Length - read, token); + } + + uint length = BitConverter.ToUInt32(header, 0); + byte[] nonce = header.Skip(4).ToArray(); + + if (length > MAX_MESSAGE_LEN) { + throw new ArgumentOutOfRangeException($"Encrypted message specified a size of {length}, which is greater than the limit of {MAX_MESSAGE_LEN}"); + } + + byte[] ciphertext = new byte[length]; + read = 0; + while (read < ciphertext.Length) { + read += await s.ReadAsync(ciphertext, read, ciphertext.Length - read, token); + } + + return SecretBox.Open(ciphertext, nonce, key); + } + + public async static Task SendSecretMessage(Stream s, byte[] key, byte[] message, CancellationToken token = default) { + byte[] nonce = SecretBox.GenerateNonce(); + byte[] ciphertext = SecretBox.Create(message, nonce, key); + byte[] len = BitConverter.GetBytes((uint)ciphertext.Length); + + if (ciphertext.Length > MAX_MESSAGE_LEN) { + throw new ArgumentOutOfRangeException($"Encrypted message would be {len} bytes long, which is larger than the limit of {MAX_MESSAGE_LEN}"); + } + + await s.WriteAsync(len, 0, len.Length, token); + await s.WriteAsync(nonce, 0, nonce.Length, token); + await s.WriteAsync(ciphertext, 0, ciphertext.Length, token); + } + + public async static Task SendSecretMessage(Stream s, byte[] key, IEncodable message, CancellationToken token = default) { + await SendSecretMessage(s, key, message.Encode(), token); + } + + public static int MacSize() => 16; + } +} diff --git a/XIVChatCommon/XIVChatCommon.csproj b/XIVChatCommon/XIVChatCommon.csproj new file mode 100644 index 0000000..7cb7888 --- /dev/null +++ b/XIVChatCommon/XIVChatCommon.csproj @@ -0,0 +1,88 @@ + + + + + Debug + AnyCPU + {6F426EAB-15D4-48AC-80D2-2D2DF2CE5EE0} + Library + Properties + XIVChatCommon + XIVChatCommon + v4.8 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + 8.0 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + 8.0 + + + + ..\packages\MessagePack.2.1.194\lib\netstandard2.0\MessagePack.dll + + + ..\packages\MessagePack.Annotations.2.1.194\lib\netstandard2.0\MessagePack.Annotations.dll + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\packages\Sodium.Core.1.2.3\lib\netstandard2.0\Sodium.Core.dll + + + + ..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/XIVChatCommon/XivString.cs b/XIVChatCommon/XivString.cs new file mode 100644 index 0000000..c01e672 --- /dev/null +++ b/XIVChatCommon/XivString.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace XIVChatCommon { + public static class XivString { + private const byte START = 2; + private const byte END = 3; + + public static List ToChunks(byte[] bytes) { + var chunks = new List(); + var stringBytes = new List(); + + var italic = false; + uint? foreground = null; + uint? glow = null; + + Action appendCurrent = (bool clear) => { + var text = Encoding.UTF8.GetString(stringBytes.ToArray()); + chunks.Add(new TextChunk { + Foreground = foreground, + Glow = glow, + Italic = italic, + Content = text, + }); + if (clear) { + stringBytes.Clear(); + } + }; + + var reader = new BinaryReader(new MemoryStream(bytes)); + while (reader.BaseStream.Position < reader.BaseStream.Length) { + var b = reader.ReadByte(); + if (b == START) { + var kind = reader.ReadByte(); // kind + var len = GetInteger(reader); // data length + var data = new BinaryReader(new MemoryStream(reader.ReadBytes((int)len))); // data + var end = reader.ReadByte(); // end + if (end != END) { + throw new ArgumentException("Input was not a valid XivString"); + } + + switch (kind) { + // icon processing + case 0x12: + var spriteIndex = GetInteger(data); + chunks.Add(new IconChunk { + Index = (byte)spriteIndex, + }); + break; + // italics processing + case 0x1a: + var newStatus = GetInteger(data) == 1; + + var appendNow = (italic && !newStatus) || (!italic && newStatus); + if (!appendNow) { + break; + } + + appendCurrent(true); + + italic = newStatus; + break; + // foreground + case 0x48: + break; + // glow + case 0x49: + break; + } + continue; + } + stringBytes.Add(b); + } + + return chunks; + } + + public static string GetText(byte[] bytes) { + var stringBytes = new List(); + + var reader = new BinaryReader(new MemoryStream(bytes)); + while (reader.BaseStream.Position < reader.BaseStream.Length) { + var b = reader.ReadByte(); + if (b == START) { + reader.ReadByte(); // kind + var len = GetInteger(reader); // data length + reader.ReadBytes((int)len); // data + var end = reader.ReadByte(); // end + if (end != END) { + throw new ArgumentException("Input was not a valid XivString"); + } + continue; + } + stringBytes.Add(b); + } + + return Encoding.UTF8.GetString(stringBytes.ToArray()); + } + + // Thanks, Dalamud + + protected enum IntegerType { + // used as an internal marker; sometimes single bytes are bare with no marker at all + None = 0, + + Byte = 0xF0, + ByteTimes256 = 0xF1, + Int16 = 0xF2, + ByteSHL16 = 0xF3, + Int16Packed = 0xF4, // seen in map links, seemingly 2 8-bit values packed into 2 bytes with only one marker + Int16SHL8 = 0xF5, + Int24Special = 0xF6, // unsure how different form Int24 - used for hq items that add 1 million, also used for normal 24-bit values in map links + Int8SHL24 = 0xF7, + Int8SHL8Int8 = 0xF8, + Int8SHL8Int8SHL8 = 0xF9, + Int24 = 0xFA, + Int16SHL16 = 0xFB, + Int24Packed = 0xFC, // used in map links- sometimes short+byte, sometimes... not?? + Int16Int8SHL8 = 0xFD, + Int32 = 0xFE + } + + private static uint GetInteger(BinaryReader input) { + var t = input.ReadByte(); + var type = (IntegerType)t; + return GetInteger(input, type); + } + + private static uint GetInteger(BinaryReader input, IntegerType type) { + const byte ByteLengthCutoff = 0xF0; + + var t = (byte)type; + if (t < ByteLengthCutoff) { + return (uint)(t - 1); + } + + switch (type) { + case IntegerType.Byte: + return input.ReadByte(); + + case IntegerType.ByteTimes256: + return input.ReadByte() * (uint)256; + case IntegerType.ByteSHL16: + return (uint)(input.ReadByte() << 16); + case IntegerType.Int8SHL24: + return (uint)(input.ReadByte() << 24); + case IntegerType.Int8SHL8Int8: { + var v = 0; + v |= input.ReadByte() << 24; + v |= input.ReadByte(); + return (uint)v; + } + case IntegerType.Int8SHL8Int8SHL8: { + var v = 0; + v |= input.ReadByte() << 24; + v |= input.ReadByte() << 8; + return (uint)v; + } + + + case IntegerType.Int16: + // fallthrough - same logic + case IntegerType.Int16Packed: { + var v = 0; + v |= input.ReadByte() << 8; + v |= input.ReadByte(); + return (uint)v; + } + case IntegerType.Int16SHL8: { + var v = 0; + v |= input.ReadByte() << 16; + v |= input.ReadByte() << 8; + return (uint)v; + } + case IntegerType.Int16SHL16: { + var v = 0; + v |= input.ReadByte() << 24; + v |= input.ReadByte() << 16; + return (uint)v; + } + + case IntegerType.Int24Special: + // Fallthrough - same logic + case IntegerType.Int24Packed: + // fallthrough again + case IntegerType.Int24: { + var v = 0; + v |= input.ReadByte() << 16; + v |= input.ReadByte() << 8; + v |= input.ReadByte(); + return (uint)v; + } + case IntegerType.Int16Int8SHL8: { + var v = 0; + v |= input.ReadByte() << 24; + v |= input.ReadByte() << 16; + v |= input.ReadByte() << 8; + return (uint)v; + } + case IntegerType.Int32: { + var v = 0; + v |= input.ReadByte() << 24; + v |= input.ReadByte() << 16; + v |= input.ReadByte() << 8; + v |= input.ReadByte(); + return (uint)v; + } + + default: + throw new NotSupportedException(); + } + } + } +} diff --git a/XIVChatCommon/app.config b/XIVChatCommon/app.config new file mode 100644 index 0000000..9274e6c --- /dev/null +++ b/XIVChatCommon/app.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/XIVChatCommon/packages.config b/XIVChatCommon/packages.config new file mode 100644 index 0000000..a97cf26 --- /dev/null +++ b/XIVChatCommon/packages.config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/XIVChatPlugin/Configuration.cs b/XIVChatPlugin/Configuration.cs new file mode 100644 index 0000000..04e50f3 --- /dev/null +++ b/XIVChatPlugin/Configuration.cs @@ -0,0 +1,29 @@ +using Dalamud.Configuration; +using Sodium; +using System; +using System.Collections.Generic; + +namespace XIVChatPlugin { + public class Configuration : IPluginConfiguration { + private Plugin plugin; + + public int Version { get; set; } = 1; + public ushort Port { get; set; } = 14777; + + public bool BacklogEnabled { get; set; } = true; + public ushort BacklogCount { get; set; } = 100; + + public bool SendBattle { get; set; } = true; + + public Dictionary> TrustedKeys { get; set; } = new Dictionary>(); + public KeyPair KeyPair { get; set; } = null; + + public void Initialise(Plugin plugin) { + this.plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null"); + } + + public void Save() { + this.plugin.Interface.SavePluginConfig(this); + } + } +} diff --git a/XIVChatPlugin/Extensions.cs b/XIVChatPlugin/Extensions.cs new file mode 100644 index 0000000..ce81d88 --- /dev/null +++ b/XIVChatPlugin/Extensions.cs @@ -0,0 +1,65 @@ +using Dalamud.Plugin; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +namespace XIVChatPlugin { + public static class Extensions { + public static string ToHexString(this byte[] bytes, bool upper = false, string separator = "") { + return string.Join(separator, bytes.Select(b => b.ToString(upper ? "X2" : "x2"))); + } + + //public static List ToColours(this byte[] bytes) { + // var colours = new List(); + + // uint colour = 0xFF; + // for (int i = 0; i < bytes.Length; i++) { + // var idx = i % 3; + + // if (i != 0 && idx == 0) { + // colours.Add(colour); + // colour = 0xFF; + // } + + // colour |= (uint)bytes[i] << ((4 - idx - 1) * 8); + // } + + // colours.Add(colour); + + // return colours; + //} + + public static List ToColours(this byte[] bytes) { + var colours = new List(); + + var colour = new Vector4(0f, 0f, 0f, 1f); + for (int i = 0; i < bytes.Length; i++) { + var idx = i % 3; + + if (i != 0 && idx == 0) { + colours.Add(colour); + colour = new Vector4(0f, 0f, 0f, 1f); + } + + switch (idx) { + case 0: + colour.X = bytes[i] / 255f; + break; + case 1: + colour.Y = bytes[i] / 255f; + break; + case 2: + colour.Z = bytes[i] / 255f; + break; + default: + throw new ApplicationException("unreachable code reached"); + } + } + + colours.Add(colour); + + return colours; + } + } +} diff --git a/XIVChatPlugin/GameFunctions.cs b/XIVChatPlugin/GameFunctions.cs new file mode 100644 index 0000000..19a9882 --- /dev/null +++ b/XIVChatPlugin/GameFunctions.cs @@ -0,0 +1,230 @@ +using Dalamud.Hooking; +using Lumina.Excel.GeneratedSheets; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using XIVChatCommon; + +namespace XIVChatPlugin { + public class GameFunctions : IDisposable { + private readonly Plugin plugin; + + private delegate IntPtr GetUIBaseDelegate(); + private delegate IntPtr GetUIModuleDelegate(IntPtr basePtr); + private delegate void EasierProcessChatBoxDelegate(IntPtr uiModule, IntPtr message, IntPtr unused, byte a4); + private delegate byte RequestFriendListDelegate(IntPtr manager); + private delegate int FormatFriendListNameDelegate(long a1, long a2, long a3, int a4, IntPtr data, long a6); + private delegate IntPtr OnReceiveFriendListChunkDelegate(IntPtr a1, IntPtr data); + + private readonly Hook friendListHook; + private readonly Hook formatHook; + private readonly Hook receiveChunkHook; + + private readonly GetUIModuleDelegate GetUIModule; + private readonly EasierProcessChatBoxDelegate _EasierProcessChatBox; + + private readonly IntPtr uiModulePtr; + private IntPtr friendListManager = IntPtr.Zero; + + private bool requestingFriendList = false; + public bool RequestingFriendList => this.requestingFriendList; + private readonly List friends = new List(); + + public delegate void ReceiveFriendListHandler(List friends); + public event ReceiveFriendListHandler ReceiveFriendList; + + public GameFunctions(Plugin plugin) { + this.plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null"); + + var getUIModulePtr = this.plugin.Interface.TargetModuleScanner.ScanText("E8 ?? ?? ?? ?? 48 83 7F ?? 00 48 8B F0"); + var easierProcessChatBoxPtr = this.plugin.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 45 84 C9"); + var friendListPtr = this.plugin.Interface.TargetModuleScanner.ScanText("40 53 48 81 EC 80 0F 00 00 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 48 8B D9 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0 0F 84 ?? ?? ?? ?? 44 0F B6 43 ?? 33 C9"); + var formatPtr = this.plugin.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 41 56 48 83 EC 30 48 8B 6C 24 ??"); + var recvChunkPtr = this.plugin.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 56 48 83 EC 20 48 8B 0D ?? ?? ?? ?? 48 8B F2"); + this.uiModulePtr = this.plugin.Interface.TargetModuleScanner.GetStaticAddressFromSig("48 8B 0D ?? ?? ?? ?? 48 8D 54 24 ?? 48 83 C1 10 E8 ?? ?? ?? ??"); + + this.GetUIModule = Marshal.GetDelegateForFunctionPointer(getUIModulePtr); + this._EasierProcessChatBox = Marshal.GetDelegateForFunctionPointer(easierProcessChatBoxPtr); + + this.friendListHook = new Hook(friendListPtr, new RequestFriendListDelegate(this.OnRequestFriendList)); + this.formatHook = new Hook(formatPtr, new FormatFriendListNameDelegate(this.OnFormatFriendList)); + this.receiveChunkHook = new Hook(recvChunkPtr, new OnReceiveFriendListChunkDelegate(this.OnReceiveFriendList)); + + this.friendListHook.Enable(); + this.formatHook.Enable(); + this.receiveChunkHook.Enable(); + } + + public void ProcessChatBox(string message) { + IntPtr uiModule = this.GetUIModule(Marshal.ReadIntPtr(this.uiModulePtr)); + + if (uiModule == IntPtr.Zero) { + throw new ApplicationException("uiModule was null"); + } + + using (var payload = new ChatPayload(message)) { + IntPtr mem1 = Marshal.AllocHGlobal(400); + Marshal.StructureToPtr(payload, mem1, false); + + this._EasierProcessChatBox(uiModule, mem1, IntPtr.Zero, 0); + + Marshal.FreeHGlobal(mem1); + } + } + + public bool RequestFriendList() { + if (this.friendListManager == IntPtr.Zero || this.friendListHook == null) { + return false; + } + + this.requestingFriendList = true; + this.friendListHook.Original(this.friendListManager); + return true; + } + + private byte OnRequestFriendList(IntPtr manager) { + this.friendListManager = manager; + return this.friendListHook.Original(manager); + } + + private int OnFormatFriendList(long a1, long a2, long a3, int a4, IntPtr data, long a6) { + // have to call this first to populate cross-world info + var ret = this.formatHook.Original(a1, a2, a3, a4, data, a6); + + if (!this.RequestingFriendList) { + return ret; + } + + var entry = Marshal.PtrToStructure(data); + + string jobName = null; + if (entry.job > 0) { + jobName = this.plugin.Interface.Data.GetExcelSheet().GetRow(entry.job)?.Name; + } + + var player = new Player { + Name = entry.Name(), + FreeCompany = entry.FreeCompany(), + Status = entry.flags, + + CurrentWorld = entry.currentWorldId, + CurrentWorldName = this.plugin.Interface.Data.GetExcelSheet().GetRow(entry.currentWorldId)?.Name, + HomeWorld = entry.homeWorldId, + HomeWorldName = this.plugin.Interface.Data.GetExcelSheet().GetRow(entry.homeWorldId)?.Name, + + Territory = entry.territoryId, + TerritoryName = this.plugin.Interface.Data.GetExcelSheet().GetRow(entry.territoryId)?.PlaceName?.Value?.Name, + + Job = entry.job, + JobName = jobName, + + GrandCompany = entry.grandCompany, + GrandCompanyName = this.plugin.Interface.Data.GetExcelSheet().GetRow(entry.grandCompany)?.Name, + + Languages = entry.langsEnabled, + MainLanguage = entry.mainLanguage, + }; + this.friends.Add(player); + + return ret; + } + + private IntPtr OnReceiveFriendList(IntPtr a1, IntPtr data) { + var ret = this.receiveChunkHook.Original(a1, data); + + // + 0xc + // 1 = party + // 2 = friends + // 3 = linkshell + // doesn't run (though same memory gets updated) for cwl or blacklist + + // + 0x8 is current number of results returned or 0 when end of list + + if (!this.RequestingFriendList) { + goto Return; + } + + if (Marshal.ReadByte(data + 0xc) != 2 || Marshal.ReadInt16(data + 0x8) != 0) { + goto Return; + } + + this.ReceiveFriendList(this.friends); + this.friends.Clear(); + this.requestingFriendList = false; + + Return: + return ret; + } + + public void Dispose() { + this.friendListHook?.Dispose(); + this.formatHook?.Dispose(); + this.receiveChunkHook?.Dispose(); + } + } + + [StructLayout(LayoutKind.Explicit)] + struct ChatPayload : IDisposable { + [FieldOffset(0)] + readonly IntPtr textPtr; + [FieldOffset(16)] + readonly ulong textLen; + + [FieldOffset(8)] + readonly ulong unk1; + [FieldOffset(24)] + readonly ulong unk2; + + internal ChatPayload(string text) { + byte[] stringBytes = Encoding.UTF8.GetBytes(text); + this.textPtr = Marshal.AllocHGlobal(stringBytes.Length + 30); + Marshal.Copy(stringBytes, 0, this.textPtr, stringBytes.Length); + Marshal.WriteByte(this.textPtr + stringBytes.Length, 0); + + this.textLen = (ulong)(stringBytes.Length + 1); + + this.unk1 = 64; + this.unk2 = 0; + } + + public void Dispose() { + Marshal.FreeHGlobal(this.textPtr); + } + } + + [StructLayout(LayoutKind.Sequential)] + struct FriendListEntryRaw { + readonly ulong unk1; + internal ulong flags; + readonly uint unk2; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + readonly byte[] unk3; + internal readonly ushort currentWorldId; + internal readonly ushort homeWorldId; + internal readonly ushort territoryId; + internal readonly byte grandCompany; + internal readonly byte mainLanguage; + internal readonly byte langsEnabled; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + readonly byte[] unk4; + internal readonly byte job; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + readonly byte[] name; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + readonly byte[] fc; + + private static string HandleString(byte[] bytes) { + byte[] nonNull = bytes.TakeWhile(b => b != 0).ToArray(); + if (nonNull.Length == 0) { + return null; + } + + return Encoding.UTF8.GetString(nonNull); + } + + public string Name() => HandleString(this.name); + public string FreeCompany() => HandleString(this.fc); + } +} diff --git a/XIVChatPlugin/ILMerge.props b/XIVChatPlugin/ILMerge.props new file mode 100644 index 0000000..e50312a --- /dev/null +++ b/XIVChatPlugin/ILMerge.props @@ -0,0 +1,67 @@ + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/XIVChatPlugin/ILMergeOrder.txt b/XIVChatPlugin/ILMergeOrder.txt new file mode 100644 index 0000000..3fda7f5 --- /dev/null +++ b/XIVChatPlugin/ILMergeOrder.txt @@ -0,0 +1,4 @@ +# this file contains the partial list of the merged assemblies in the merge order +# you can fill it from the obj\CONFIG\PROJECT.ilmerge generated on every build +# and finetune merge order to your satisfaction + diff --git a/XIVChatPlugin/Plugin.cs b/XIVChatPlugin/Plugin.cs new file mode 100644 index 0000000..30251fc --- /dev/null +++ b/XIVChatPlugin/Plugin.cs @@ -0,0 +1,106 @@ +using Dalamud.Game.Command; +using Dalamud.Hooking; +using Dalamud.Plugin; +using System; +using System.IO; +using System.Reflection; + +namespace XIVChatPlugin { + public class Plugin : IDalamudPlugin { + private bool disposedValue; + + public string Name => "XIVChat"; + + internal string Location { get; private set; } = Assembly.GetExecutingAssembly().Location; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "LivePluginLoader")] + private void SetLocation(string path) { + this.Location = path; + } + + public DalamudPluginInterface Interface { get; private set; } + public Configuration Config { get; private set; } + public PluginUI Ui { get; private set; } + public Server Server { get; private set; } + public GameFunctions Functions { get; private set; } + + private delegate byte ChatChannelChangeDelegate(IntPtr a1, uint channel); + private Hook chatChannelChangeHook; + + public void Initialize(DalamudPluginInterface pluginInterface) { + this.Interface = pluginInterface ?? throw new ArgumentNullException(nameof(pluginInterface), "DalamudPluginInterface cannot be null"); + + // load libsodium.so from debug location if in debug mode +#if DEBUG + string path = Environment.GetEnvironmentVariable("PATH"); + string newPath = Path.GetDirectoryName(this.Location); + Environment.SetEnvironmentVariable("PATH", $"{path};{newPath}"); +#endif + + this.Config = (Configuration)this.Interface.GetPluginConfig() ?? new Configuration(); + this.Config.Initialise(this); + + this.Functions = new GameFunctions(this); + var funcPtr = this.Interface.TargetModuleScanner.ScanText("40 55 48 8D 6C 24 ?? 48 81 EC A0 00 00 00 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 45 ?? 48 8B 0D ?? ?? ?? ?? 33 C0 48 83 C1 10 89 45 ?? C7 45 ?? 01 00 00 00"); + if (funcPtr == IntPtr.Zero) { + PluginLog.LogError("Could not sig chat channel change function"); + } else { + this.chatChannelChangeHook = new Hook(funcPtr, new ChatChannelChangeDelegate(this.ChangeChatChannelDetour)); + this.chatChannelChangeHook.Enable(); + } + + this.Ui = new PluginUI(this); + + this.Server = new Server(this); + this.Server.Spawn(); + + this.Interface.UiBuilder.OnBuildUi += this.Ui.Draw; + this.Interface.UiBuilder.OnOpenConfigUi += this.Ui.OpenSettings; + this.Interface.Framework.OnUpdateEvent += this.Server.OnFrameworkUpdate; + this.Interface.Framework.Gui.Chat.OnChatMessage += this.Server.OnChat; + this.Interface.ClientState.OnLogin += this.Server.OnLogIn; + this.Interface.ClientState.OnLogout += this.Server.OnLogOut; + this.Interface.ClientState.TerritoryChanged += this.Server.OnTerritoryChange; + this.Interface.CommandManager.AddHandler("/xivchat", new CommandInfo(this.OnCommand) { + HelpMessage = "Opens the config for the XIVChat plugin", + }); + } + + private byte ChangeChatChannelDetour(IntPtr a1, uint channel) { + // a1 + 0xfd0 is the chat channel byte (including for when clicking on shout) + this.Server.OnChatChannelChange(channel); + return this.chatChannelChangeHook.Original(a1, channel); + } + + private void OnCommand(string command, string args) { + this.Ui.OpenSettings(null, null); + } + + protected virtual void Dispose(bool disposing) { + if (!this.disposedValue) { + if (disposing) { + this.Server.Dispose(); + + this.Interface.UiBuilder.OnBuildUi -= this.Ui.Draw; + this.Interface.UiBuilder.OnOpenConfigUi -= this.Ui.OpenSettings; + this.Interface.Framework.OnUpdateEvent -= this.Server.OnFrameworkUpdate; + this.Interface.Framework.Gui.Chat.OnChatMessage -= this.Server.OnChat; + this.Interface.ClientState.OnLogin -= this.Server.OnLogIn; + this.Interface.ClientState.OnLogout -= this.Server.OnLogOut; + this.Interface.ClientState.TerritoryChanged -= this.Server.OnTerritoryChange; + this.Interface.CommandManager.RemoveHandler("/xivchat"); + + this.chatChannelChangeHook?.Dispose(); + } + + this.disposedValue = true; + } + } + + public void Dispose() { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/XIVChatPlugin/PluginUI.cs b/XIVChatPlugin/PluginUI.cs new file mode 100644 index 0000000..4a763b1 --- /dev/null +++ b/XIVChatPlugin/PluginUI.cs @@ -0,0 +1,345 @@ +using ImGuiNET; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Numerics; +using System.Threading.Channels; + +namespace XIVChatPlugin { + public class PluginUI { + private readonly Plugin plugin; + + private bool _showSettings = false; + public bool ShowSettings { get => this._showSettings; set => this._showSettings = value; } + + private readonly Dictionary>> pending = new Dictionary>>(); + private readonly Dictionary pendingNames = new Dictionary(0); + + public PluginUI(Plugin plugin) { + this.plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null"); + } + + private static class Colours { + public static readonly Vector4 primary = new Vector4(2 / 255f, 204 / 255f, 238 / 255f, 1.0f); + public static readonly Vector4 primaryDark = new Vector4(2 / 255f, 180 / 255f, 211 / 255f, 1.0f); + public static readonly Vector4 background = new Vector4(46 / 255f, 46 / 255f, 46 / 255f, 1.0f); + public static readonly Vector4 text = new Vector4(190 / 255f, 190 / 255f, 190 / 255f, 1.0f); + public static readonly Vector4 button = new Vector4(90 / 255f, 89 / 255f, 90 / 255f, 1.0f); + public static readonly Vector4 buttonActive = new Vector4(123 / 255f, 122 / 255f, 124 / 255f, 1.0f); + public static readonly Vector4 buttonHovered = new Vector4(108 / 255f, 107 / 255f, 109 / 255f, 1.0f); + + public static readonly Vector4 white = new Vector4(1f, 1f, 1f, 1f); + } + + public void Draw() { + ImGui.PushStyleColor(ImGuiCol.TitleBg, Colours.primaryDark); + ImGui.PushStyleColor(ImGuiCol.TitleBgActive, Colours.primary); + ImGui.PushStyleColor(ImGuiCol.TitleBgCollapsed, Colours.primaryDark); + ImGui.PushStyleColor(ImGuiCol.WindowBg, Colours.background); + ImGui.PushStyleColor(ImGuiCol.Text, Colours.text); + ImGui.PushStyleColor(ImGuiCol.Button, Colours.button); + ImGui.PushStyleColor(ImGuiCol.ButtonActive, Colours.buttonActive); + ImGui.PushStyleColor(ImGuiCol.ButtonHovered, Colours.buttonHovered); + + this.DrawInner(); + + ImGui.PopStyleColor(8); + } + + private static V WithWhiteText(Func func) { + ImGui.PushStyleColor(ImGuiCol.Text, Colours.white); + var ret = func(); + ImGui.PopStyleColor(); + return ret; + } + + private static void WithWhiteText(Action func) { + ImGui.PushStyleColor(ImGuiCol.Text, Colours.white); + func(); + ImGui.PopStyleColor(); + } + + private static bool Begin(string name, ImGuiWindowFlags flags) { + return WithWhiteText(() => ImGui.Begin(name, flags)); + } + + private static bool Begin(string name, ref bool showSettings, ImGuiWindowFlags flags) { + ImGui.PushStyleColor(ImGuiCol.Text, Colours.white); + var result = ImGui.Begin(name, ref showSettings, flags); + ImGui.PopStyleColor(); + return result; + } + + private static void TextWhite(string text) => WithWhiteText(() => ImGui.TextUnformatted(text)); + + private void DrawInner() { + this.AcceptPending(); + + foreach (var item in this.pending.ToList()) { + if (this.DrawPending(item.Key, item.Value.Item1, item.Value.Item2)) { + this.pending.Remove(item.Key); + } + } + + if (!this.ShowSettings || !Begin(this.plugin.Name, ref this._showSettings, ImGuiWindowFlags.AlwaysAutoResize)) { + return; + } + + if (WithWhiteText(() => ImGui.CollapsingHeader("Server public key"))) { + string serverPublic = this.plugin.Config.KeyPair.PublicKey.ToHexString(upper: true); + ImGui.TextUnformatted(serverPublic); + this.DrawColours(this.plugin.Config.KeyPair.PublicKey, serverPublic); + + if (WithWhiteText(() => ImGui.Button("Regenerate"))) { + this.plugin.Server.RegenerateKeyPair(); + } + } + + if (WithWhiteText(() => ImGui.CollapsingHeader("Settings", ImGuiTreeNodeFlags.DefaultOpen))) { + TextWhite("Port"); + + int port = this.plugin.Config.Port; + if (WithWhiteText(() => ImGui.InputInt("##port", ref port))) { + ushort realPort = (ushort)Math.Min(ushort.MaxValue, Math.Max(1, port)); + this.plugin.Config.Port = realPort; + this.plugin.Config.Save(); + } + + ImGui.Spacing(); + + bool backlogEnabled = this.plugin.Config.BacklogEnabled; + if (WithWhiteText(() => ImGui.Checkbox("Enable backlog", ref backlogEnabled))) { + this.plugin.Config.BacklogEnabled = backlogEnabled; + this.plugin.Config.Save(); + } + + int backlogCount = this.plugin.Config.BacklogCount; + if (WithWhiteText(() => ImGui.DragInt("Backlog messages", ref backlogCount, 1f, 0, ushort.MaxValue))) { + this.plugin.Config.BacklogCount = (ushort)Math.Max(0, Math.Min(ushort.MaxValue, backlogCount)); + this.plugin.Config.Save(); + } + + ImGui.Spacing(); + + bool sendBattle = this.plugin.Config.SendBattle; + if (WithWhiteText(() => ImGui.Checkbox("Send battle messages", ref sendBattle))) { + this.plugin.Config.SendBattle = sendBattle; + this.plugin.Config.Save(); + } + + ImGui.TextUnformatted("Changing this setting will not affect messages already in the backlog."); + } + + if (WithWhiteText(() => ImGui.CollapsingHeader("Trusted keys"))) { + if (this.plugin.Config.TrustedKeys.Count == 0) { + ImGui.TextUnformatted("None"); + } + + ImGui.Columns(2); + var maxKeyLength = 0f; + foreach (var entry in this.plugin.Config.TrustedKeys.ToList()) { + var name = entry.Value.Item1; + + var key = entry.Value.Item2; + var hex = key.ToHexString(upper: true); + + maxKeyLength = Math.Max(maxKeyLength, ImGui.CalcTextSize(name).X); + + ImGui.TextUnformatted(name); + if (ImGui.IsItemHovered()) { + ImGui.BeginTooltip(); + ImGui.TextUnformatted(hex); + this.DrawColours(key, hex); + ImGui.EndTooltip(); + } + ImGui.NextColumn(); + + if (WithWhiteText(() => ImGui.Button($"Untrust##{entry.Key}"))) { + this.plugin.Config.TrustedKeys.Remove(entry.Key); + this.plugin.Config.Save(); + } + ImGui.NextColumn(); + } + ImGui.SetColumnWidth(0, maxKeyLength + ImGui.GetStyle().ItemSpacing.X * 2); + ImGui.Columns(1); + } + + + if (WithWhiteText(() => ImGui.CollapsingHeader("Connected clients"))) { + if (this.plugin.Server.Clients.Count == 0) { + ImGui.TextUnformatted("None"); + } else { + ImGui.Columns(3); + + TextWhite("IP"); + ImGui.NextColumn(); + TextWhite("Key"); + ImGui.NextColumn(); + ImGui.NextColumn(); + + foreach (var client in this.plugin.Server.Clients) { + EndPoint remote; + try { + remote = client.Value.Conn.Client.RemoteEndPoint; + } catch (ObjectDisposedException) { + continue; + } + string ipAddress; + if (remote is IPEndPoint ip) { + ipAddress = ip.Address.ToString(); + } else { + ipAddress = "Unknown"; + } + ImGui.TextUnformatted(ipAddress); + + ImGui.NextColumn(); + + var trustedKey = this.plugin.Config.TrustedKeys.Values.FirstOrDefault(entry => entry.Item2.SequenceEqual(client.Value.Handshake.RemotePublicKey)); + if (trustedKey != default(Tuple)) { + ImGui.TextUnformatted(trustedKey.Item1); + if (ImGui.IsItemHovered()) { + ImGui.BeginTooltip(); + + var hex = trustedKey.Item2.ToHexString(upper: true); + ImGui.TextUnformatted(hex); + this.DrawColours(trustedKey.Item2, hex); + + ImGui.EndTooltip(); + } + } + ImGui.NextColumn(); + + if (WithWhiteText(() => ImGui.Button($"Disconnect##{client.Key}"))) { + client.Value.Disconnect(); + } + + ImGui.NextColumn(); + } + ImGui.Columns(1); + } + } + + if (WithWhiteText(() => ImGui.CollapsingHeader("ACT/Teamcraft issues?"))) { + ImGui.PushTextWrapPos(ImGui.GetFontSize() * 20); + ImGui.TextUnformatted("Click on the button below to visit a website showing a workaround for ACT and Teamcraft having issues."); + ImGui.PopTextWrapPos(); + + if (WithWhiteText(() => ImGui.Button("Open website"))) { + System.Diagnostics.Process.Start("https://xiv.chat/server/workaround"); + } + } + + ImGui.End(); + } + + private void DrawColours(byte[] bytes, string widthOf) { + this.DrawColours(bytes, ImGui.CalcTextSize(widthOf).X); + } + + private void DrawColours(byte[] bytes, float width = 0f) { + var pos = ImGui.GetCursorScreenPos(); + var spacing = ImGui.GetStyle().ItemSpacing; + + var colours = bytes.ToColours(); + + float sizeX = width == 0f ? 32f : width / colours.Count; + + for (int i = 0; i < colours.Count; i++) { + var topLeft = new Vector2( + pos.X + (sizeX * i), + pos.Y + spacing.Y + ); + var bottomRight = new Vector2( + pos.X + (sizeX * (i + 1)), + pos.Y + spacing.Y + 16 + ); + + ImGui.GetWindowDrawList().AddRectFilled( + topLeft, + bottomRight, + ImGui.GetColorU32(colours[i]) + ); + } + + // create a spacing for 32px and spacing + ImGui.Dummy(new Vector2(0, 16 + spacing.Y * 2)); + } + + public void OpenSettings(object sender, EventArgs args) { + this.ShowSettings = true; + } + + private void AcceptPending() { + while (this.plugin.Server.pendingClients.Reader.TryRead(out var item)) { + this.pending[Guid.NewGuid()] = item; + } + } + + private bool DrawPending(Guid id, Client client, Channel accepted) { + bool ret = false; + + var clientPublic = client.Handshake.RemotePublicKey; + var clientPublicHex = clientPublic.ToHexString(upper: true); + var serverPublic = this.plugin.Config.KeyPair.PublicKey; + var serverPublicHex = serverPublic.ToHexString(upper: true); + + var width = Math.Max(ImGui.CalcTextSize(clientPublicHex).X, ImGui.CalcTextSize(serverPublicHex).X) + (ImGui.GetStyle().WindowPadding.X * 2); + + if (!Begin($"Incoming XIVChat connection##{clientPublic}", ImGuiWindowFlags.AlwaysAutoResize)) { + return ret; + } + + ImGui.PushTextWrapPos(width); + + ImGui.TextUnformatted("A client that has not previously connected is attempting to connect to XIVChat. If this is you, please check the two keys below and make sure that they match what is displayed by the client."); + + ImGui.Separator(); + + TextWhite("Server"); + ImGui.TextUnformatted(serverPublicHex); + this.DrawColours(serverPublic, serverPublicHex); + + ImGui.Spacing(); + + TextWhite("Client"); + ImGui.TextUnformatted(clientPublicHex); + this.DrawColours(clientPublic, clientPublicHex); + + ImGui.Separator(); + + ImGui.TextUnformatted("Give this client a name to remember it more easily if you trust it."); + + ImGui.PopTextWrapPos(); + + if (!this.pendingNames.TryGetValue(id, out string name)) { + name = "No name"; + } + + if (WithWhiteText(() => ImGui.InputText("Client name", ref name, 100, ImGuiInputTextFlags.AutoSelectAll))) { + this.pendingNames[id] = name; + } + + ImGui.Separator(); + + ImGui.TextUnformatted("Do both keys match?"); + if (WithWhiteText(() => ImGui.Button("Yes"))) { + accepted.Writer.TryWrite(true); + this.plugin.Config.TrustedKeys[Guid.NewGuid()] = Tuple.Create(name, client.Handshake.RemotePublicKey); + this.plugin.Config.Save(); + this.pendingNames.Remove(id); + ret = true; + } + ImGui.SameLine(); + if (WithWhiteText(() => ImGui.Button("No"))) { + accepted.Writer.TryWrite(false); + this.pendingNames.Remove(id); + ret = true; + } + + ImGui.End(); + + return ret; + } + } +} diff --git a/XIVChatPlugin/Properties/AssemblyInfo.cs b/XIVChatPlugin/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..3433a3f --- /dev/null +++ b/XIVChatPlugin/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("XIVChatPlugin")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("XIVChatPlugin")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9aeb2b77-c127-4ba8-b9ae-fb3ca1649fdb")] + +// 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")] +[assembly: AssemblyFileVersion("1.0.0")] diff --git a/XIVChatPlugin/Resources/lib/libsodium.dll b/XIVChatPlugin/Resources/lib/libsodium.dll new file mode 100644 index 0000000000000000000000000000000000000000..4202c3a2ebbb4746a711eca5fa8fafeb1b1b0b75 GIT binary patch literal 303604 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 toGame = new ConcurrentQueue(); + + private readonly ConcurrentDictionary clients = new ConcurrentDictionary(); + public IReadOnlyDictionary Clients => this.clients; + public readonly Channel>> pendingClients = Channel.CreateUnbounded>>(); + + private readonly HashSet WaitingForFriendList = new HashSet(); + + private readonly LinkedList Backlog = new LinkedList(); + + private TcpListener listener; + + private bool sendPlayerData = false; + + private volatile bool _running = false; + public bool Running { get => this._running; set => this._running = value; } + + private InputChannel currentChannel = InputChannel.Say; + + private const int MAX_MESSAGE_SIZE = 128_000; + + public Server(Plugin plugin) { + this.plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null"); + if (this.plugin.Config.KeyPair == null) { + this.RegenerateKeyPair(); + } + + this.plugin.Functions.ReceiveFriendList += this.OnReceiveFriendList; + } + + private async void OnReceiveFriendList(List friends) { + var msg = new ServerPlayerList { + Type = PlayerListType.Friend, + Players = friends.ToArray(), + }; + + foreach (var id in this.WaitingForFriendList) { + if (!this.Clients.TryGetValue(id, out var client)) { + continue; + } + + try { + await SecretMessage.SendSecretMessage(client.Conn.GetStream(), client.Handshake.Keys.tx, msg, client.TokenSource.Token); + } catch (Exception ex) { + PluginLog.LogError($"Could not send message: {ex.Message}"); + } + } + + this.WaitingForFriendList.Clear(); + } + + public void Spawn() { + var ip = IPAddress.Parse("0.0.0.0"); + var port = this.plugin.Config.Port; + + Task.Run(async () => { + this.listener = new TcpListener(ip, port); + this.listener.Start(); + + this._running = true; + PluginLog.Log("Running..."); + while (!this.tokenSource.IsCancellationRequested) { + var conn = await this.listener.GetTcpClient(this.tokenSource); + this.SpawnClientTask(conn); + } + this._running = false; + }); + } + + public void RegenerateKeyPair() { + this.plugin.Config.KeyPair = PublicKeyBox.GenerateKeyPair(); + this.plugin.Config.Save(); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "delegate")] + public void OnChat(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) { + if (isHandled) { + return; + } + + var chatCode = new ChatCode((ushort)type); + + if (!this.plugin.Config.SendBattle && chatCode.IsBattle()) { + return; + } + + var chunks = new List(); + + if (sender.Payloads.Count > 0) { + // FIXME: can't get format straight from game until Lumina stops returning LogKind.Format as a string (it's an SeString) + // var format = this.FormatFor(chatCode.Type); + var format = chatCode.NameFormat(); + if (format != null && format.IsPresent) { + chunks.Add(new TextChunk { + FallbackColour = chatCode.DefaultColour(), + Content = format.Before, + }); + chunks.AddRange(ToChunks(sender, chatCode.DefaultColour())); + chunks.Add(new TextChunk { + FallbackColour = chatCode.DefaultColour(), + Content = format.After, + }); + } + } + + chunks.AddRange(ToChunks(message, chatCode.DefaultColour())); + + var msg = new ServerMessage { + Timestamp = DateTime.UtcNow, + Channel = (ChatType)type, + Sender = sender.Encode(), + Content = message.Encode(), + Chunks = chunks, + }; + + this.Backlog.AddLast(msg); + while (this.Backlog.Count > this.plugin.Config.BacklogCount) { + this.Backlog.RemoveFirst(); + } + + foreach (var client in this.clients.Values) { + client.Queue.Writer.TryWrite(msg); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "delegate")] + public void OnFrameworkUpdate(Framework framework) { + if (this.sendPlayerData && this.plugin.Interface.ClientState.LocalPlayer != null) { + this.BroadcastPlayerData(); + this.sendPlayerData = false; + } + + if (!this.toGame.TryDequeue(out var message)) { + return; + } + + this.plugin.Functions.ProcessChatBox(message); + } + + private void SpawnClientTask(TcpClient conn) { + if (conn == null) { + return; + } + + Task.Run(async () => { + var stream = conn.GetStream(); + + var handshake = await KeyExchange.ServerHandshake(this.plugin.Config.KeyPair, stream); + var newClient = new Client(conn) { + Handshake = handshake, + }; + + // if this public key isn't trusted, prompt first + if (!this.plugin.Config.TrustedKeys.Values.Any(entry => entry.Item2.SequenceEqual(handshake.RemotePublicKey))) { + var accepted = Channel.CreateBounded(1); + + await this.pendingClients.Writer.WriteAsync(Tuple.Create(newClient, accepted)); + if (!await accepted.Reader.ReadAsync()) { + return; + } + } + + var id = Guid.NewGuid(); + newClient.Connected = true; + this.clients[id] = newClient; + + // send availability + var available = this.plugin.Interface.ClientState.LocalPlayer != null; + try { + await SecretMessage.SendSecretMessage(stream, handshake.Keys.tx, new Availability(available)); + } catch (Exception ex) { + PluginLog.LogError($"Could not send message: {ex.Message}"); + } + + // send player data + try { + await this.SendPlayerData(newClient); + } catch (Exception ex) { + PluginLog.LogError($"Could not send message: {ex.Message}"); + } + + // send current channel + try { + var channel = this.currentChannel; + await SecretMessage.SendSecretMessage(stream, handshake.Keys.tx, new ServerChannel( + channel, + this.LocalisedChannelName(channel) + )); + } catch (Exception ex) { + PluginLog.LogError($"Could not send message: {ex.Message}"); + } + + var listen = Task.Run(async () => { + conn.ReceiveTimeout = 5_000; + + while (this.clients.TryGetValue(id, out var client) && client.Connected && !client.TokenSource.IsCancellationRequested && conn.Connected) { + byte[] msg; + try { + msg = await SecretMessage.ReadSecretMessage(stream, handshake.Keys.rx, client.TokenSource.Token); + } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut) { + continue; + } catch (Exception ex) { + PluginLog.LogError($"Could not read message: {ex.Message}"); + continue; + } + + var op = (ClientOperation)msg[0]; + + var payload = new byte[msg.Length - 1]; + Array.Copy(msg, 1, payload, 0, payload.Length); + + switch (op) { + case ClientOperation.Ping: + try { + await SecretMessage.SendSecretMessage(stream, handshake.Keys.tx, Pong.Instance, client.TokenSource.Token); + } catch (Exception ex) { + PluginLog.LogError($"Could not send message: {ex.Message}"); + } + break; + case ClientOperation.Message: + var clientMessage = ClientMessage.Decode(payload); + foreach (var part in Wrap(clientMessage.Content)) { + this.toGame.Enqueue(part); + } + break; + case ClientOperation.Shutdown: + client.Disconnect(); + break; + case ClientOperation.Backlog: + var backlog = ClientBacklog.Decode(payload); + + var backlogMessages = new List(); + + var node = this.Backlog.Last; + while (node != null) { + if (backlogMessages.Count >= backlog.Amount) { + break; + } + + backlogMessages.Add(node.Value); + node = node.Previous; + } + + backlogMessages.Reverse(); + + await this.SendBacklogs(backlogMessages.ToArray(), stream, client); + break; + case ClientOperation.CatchUp: + var catchUp = ClientCatchUp.Decode(payload); + // I'm not sure why this needs to be done, but apparently it does + var after = catchUp.After.AddMilliseconds(1); + var msgs = this.MessagesAfter(after); + + await this.SendBacklogs(msgs, stream, client); + break; + case ClientOperation.PlayerList: + var playerList = ClientPlayerList.Decode(payload); + + if (playerList.Type == PlayerListType.Friend) { + this.WaitingForFriendList.Add(id); + + if (!this.plugin.Functions.RequestingFriendList && !this.plugin.Functions.RequestFriendList()) { + this.plugin.Interface.Framework.Gui.Chat.PrintError($"[{this.plugin.Name}] Please open your friend list to enable friend list support. You should only need to do this on initial install or after updates."); + } + } + + break; + } + } + }); + + while (this.clients.TryGetValue(id, out var client) && client.Connected && !client.TokenSource.IsCancellationRequested && conn.Connected) { + try { + var msg = await client.Queue.Reader.ReadAsync(client.TokenSource.Token); + await SecretMessage.SendSecretMessage(stream, handshake.Keys.tx, msg, client.TokenSource.Token); + } catch (Exception ex) { + PluginLog.LogError($"Could not send message: {ex.Message}"); + } + } + + try { + conn.Close(); + } catch (ObjectDisposedException) { } + + await listen; + + this.clients.TryRemove(id, out var _); + PluginLog.Log($"Client thread ended: {id}"); + }); + } + + private static readonly Regex colorRegex = new Regex(@"", RegexOptions.Compiled); + private Dictionary Formats { get; } = new Dictionary(); + + [Sheet("LogKind")] + class LogKind : IExcelRow { + public byte Unknown0; + public byte[] Format; + public bool Unknown2; + + public uint RowId { get; set; } + public uint SubRowId { get; set; } + + public void PopulateData(RowParser parser, Lumina.Lumina lumina, Language language) { + this.RowId = parser.Row; + this.SubRowId = parser.SubRow; + + this.Unknown0 = parser.ReadColumn(0); + this.Format = parser.ReadColumn(1); + this.Unknown2 = parser.ReadColumn(2); + } + } + + private NameFormatting FormatFor(ChatType type) { + if (this.Formats.TryGetValue(type, out var cached)) { + return cached; + } + + var logKind = this.plugin.Interface.Data.GetExcelSheet().GetRow((ushort)type); + + if (logKind == null) { + return null; + } + + var sestring = this.plugin.Interface.SeStringManager.Parse(logKind.Format); + + //PluginLog.Log(string.Join("", Encoding.ASCII.GetBytes(logKind.Format).Select(b => b.ToString("x2")))); + + //var sestring = this.plugin.Interface.SeStringManager.Parse(Encoding.ASCII.GetBytes(logKind.Format)); + + //var format = colorRegex.Replace(logKind.Format, "") + // .Replace("", "") + // .Replace("", "") + // .Replace("", ""); + + return NameFormatting.Empty(); + + //if (format.IndexOf("StringParameter(1)") == -1) { + // return NameFormatting.Empty(); + //} + + //var parts = format.Split(new string[] { "StringParameter(1)" }, StringSplitOptions.None); + + //var nameFormatting = NameFormatting.Of( + // before: parts[0], + // after: parts[1].Split(new string[] { "StringParameter(2)" }, StringSplitOptions.None)[0] + //); + + //this.Formats[type] = nameFormatting; + + //return nameFormatting; + } + + private async Task SendBacklogs(ServerMessage[] messages, NetworkStream stream, Client client) { + var size = 5 + SecretMessage.MacSize(); // assume 5 bytes for payload lead-in, although it's likely to be less + var responseMessages = new List(); + + async Task SendBacklog() { + var resp = new ServerBacklog(responseMessages.ToArray()); + try { + await SecretMessage.SendSecretMessage(stream, client.Handshake.Keys.tx, resp, client.TokenSource.Token); + } catch (Exception ex) { + PluginLog.LogError($"Could not send backlog: {ex.Message}"); + } + } + + foreach (var catchUpMessage in messages) { + // FIXME: this is very gross + var len = MessagePackSerializer.Serialize(catchUpMessage).Length; + // send message if it would've gone over length + if (size + len >= MAX_MESSAGE_SIZE) { + await SendBacklog(); + + size = 5 + SecretMessage.MacSize(); + responseMessages.Clear(); + + } + size += len; + responseMessages.Add(catchUpMessage); + } + + if (responseMessages.Count > 0) { + await SendBacklog(); + } + } + + private static List ToChunks(SeString msg, uint? defaultColour) { + var chunks = new List(); + + var italic = false; + var foreground = new Stack(); + var glow = new Stack(); + + void Append(string text) { + chunks.Add(new TextChunk { + FallbackColour = defaultColour, + Foreground = foreground.Count > 0 ? foreground.Peek() : (uint?)null, + Glow = glow.Count > 0 ? glow.Peek() : (uint?)null, + Italic = italic, + Content = text, + }); + } + + foreach (var payload in msg.Payloads) { + switch (payload.Type) { + case PayloadType.EmphasisItalic: + var newStatus = ((EmphasisItalicPayload)payload).IsEnabled; + italic = newStatus; + break; + case PayloadType.UIForeground: + var foregroundPayload = (UIForegroundPayload)payload; + if (foregroundPayload.IsEnabled) { + foreground.Push(foregroundPayload.UIColor.UIForeground); + } else if (foreground.Count > 0) { + foreground.Pop(); + } + break; + case PayloadType.UIGlow: + var glowPayload = (UIGlowPayload)payload; + if (glowPayload.IsEnabled) { + glow.Push(glowPayload.UIColor.UIGlow); + } else if (glow.Count > 0) { + glow.Pop(); + } + break; + case PayloadType.AutoTranslateText: + chunks.Add(new IconChunk { Index = 54 }); + var autoText = ((AutoTranslatePayload)payload).Text; + Append(autoText.Substring(2, autoText.Length - 4)); + chunks.Add(new IconChunk { Index = 55 }); + break; + case PayloadType.Icon: + var index = ((IconPayload)payload).IconIndex; + chunks.Add(new IconChunk { Index = (byte)index }); + break; + // FIXME: use ITextProvider directly once it's exposed + case PayloadType.RawText: + Append(((TextPayload)payload).Text); + break; + case PayloadType.Unknown: + var rawPayload = (RawPayload)payload; + if (rawPayload.Data[1] == 0x13) { + foreground.Pop(); + glow.Pop(); + } + break; + //default: + // var textProviderType = typeof(SeString).Assembly.GetType("Dalamud.Game.Chat.SeStringHandling.ITextProvider"); + // var textProp = textProviderType.GetProperty("Text", BindingFlags.NonPublic | BindingFlags.Instance); + // var text = (string)textProp.GetValue(payload); + // append(text); + // break; + } + } + + return chunks; + } + + private ServerMessage[] MessagesAfter(DateTime time) => this.Backlog.Where(msg => msg.Timestamp > time).ToArray(); + + private static string[] Wrap(string input) { + const int LIMIT = 500; + + if (input.Length <= LIMIT) { + return new string[] { input }; + } + + string prefix = string.Empty; + if (input.StartsWith("/")) { + var space = input.IndexOf(' '); + if (space != -1) { + prefix = input.Substring(0, space); + input = input.Substring(space + 1); + } + } + + var parts = new List(); + + var builder = new StringBuilder(LIMIT); + + foreach (var word in input.Split(' ')) { + if (word.Length > LIMIT) { + int wordParts = (int)Math.Ceiling((float)word.Length / LIMIT); + for (int i = 0; i < wordParts; i++) { + var start = i == 0 ? 0 : (i * LIMIT); + var partLength = LIMIT; + if (prefix.Length != 0) { + start = start == 0 ? 0 : (start - (prefix.Length + 1) * i); + partLength = partLength - prefix.Length - 1; + } + + var part = word.Length - start < partLength ? word.Substring(start) : word.Substring(start, partLength); + if (part.Length == 0) { + continue; + } + + if (prefix.Length != 0) { + part = prefix + " " + part; + } + + parts.Add(part); + } + continue; + } + + if (builder.Length + word.Length > LIMIT) { + parts.Add(builder.ToString().TrimEnd(' ')); + builder.Clear(); + } + + if (builder.Length == 0 && prefix.Length != 0) { + builder.Append(prefix); + builder.Append(' '); + } + + builder.Append(word); + builder.Append(' '); + } + + if (builder.Length != 0) { + parts.Add(builder.ToString().TrimEnd(' ')); + } + + return parts.ToArray(); + } + + private void BroadcastMessage(IEncodable message) { + foreach (var client in this.Clients.Values) { + if (client.Handshake == null || client.Conn == null) { + continue; + } + + Task.Run(async () => { + await SecretMessage.SendSecretMessage(client.Conn.GetStream(), client.Handshake.Keys.tx, message); + }); + } + } + + private string LocalisedChannelName(InputChannel channel) { + uint rowId; + switch (channel) { + case InputChannel.Tell: + rowId = 3; + break; + case InputChannel.Say: + rowId = 1; + break; + case InputChannel.Party: + rowId = 4; + break; + case InputChannel.Alliance: + rowId = 17; + break; + case InputChannel.Yell: + rowId = 16; + break; + case InputChannel.Shout: + rowId = 2; + break; + case InputChannel.FreeCompany: + rowId = 7; + break; + case InputChannel.PvpTeam: + rowId = 19; + break; + case InputChannel.NoviceNetwork: + rowId = 18; + break; + case InputChannel.CrossLinkshell1: + rowId = 20; + break; + case InputChannel.CrossLinkshell2: + rowId = 300; + break; + case InputChannel.CrossLinkshell3: + rowId = 301; + break; + case InputChannel.CrossLinkshell4: + rowId = 302; + break; + case InputChannel.CrossLinkshell5: + rowId = 303; + break; + case InputChannel.CrossLinkshell6: + rowId = 304; + break; + case InputChannel.CrossLinkshell7: + rowId = 305; + break; + case InputChannel.CrossLinkshell8: + rowId = 306; + break; + case InputChannel.Linkshell1: + rowId = 8; + break; + case InputChannel.Linkshell2: + rowId = 9; + break; + case InputChannel.Linkshell3: + rowId = 10; + break; + case InputChannel.Linkshell4: + rowId = 11; + break; + case InputChannel.Linkshell5: + rowId = 12; + break; + case InputChannel.Linkshell6: + rowId = 13; + break; + case InputChannel.Linkshell7: + rowId = 14; + break; + case InputChannel.Linkshell8: + rowId = 15; + break; + default: + rowId = 0; + break; + }; + return this.plugin.Interface.Data.GetExcelSheet().GetRow(rowId).Name; + } + + public void OnChatChannelChange(uint channel) { + var inputChannel = (InputChannel)channel; + this.currentChannel = inputChannel; + + var localisedName = this.LocalisedChannelName(inputChannel); + + var msg = new ServerChannel(inputChannel, localisedName); + this.BroadcastMessage(msg); + } + + private void BroadcastAvailability(bool available) { + this.BroadcastMessage(new Availability(available)); + } + + private PlayerData GeneratePlayerData() { + var player = this.plugin.Interface.ClientState.LocalPlayer; + if (player == null) { + return null; + } + + var homeWorld = player.HomeWorld.GameData.Name; + var currentWorld = player.CurrentWorld.GameData.Name; + var territory = this.plugin.Interface.Data.GetExcelSheet().GetRow(this.plugin.Interface.ClientState.TerritoryType); + var location = territory.PlaceName.Value.Name; + var name = player.Name; + + return new PlayerData(homeWorld, currentWorld, location, name); + } + + private async Task SendPlayerData(Client client) { + var playerData = this.GeneratePlayerData(); + if (playerData == null) { + return; + } + await SecretMessage.SendSecretMessage(client.Conn.GetStream(), client.Handshake.Keys.tx, playerData); + } + + private void BroadcastPlayerData() { + var playerData = this.GeneratePlayerData(); + + if (playerData == null) { + this.BroadcastMessage(EmptyPlayerData.Instance); + return; + } + + this.BroadcastMessage(playerData); + } + + public void OnLogIn(object sender, EventArgs e) { + this.BroadcastAvailability(true); + // send player data on next framework update + this.sendPlayerData = true; + } + + public void OnLogOut(object sender, EventArgs e) { + this.BroadcastAvailability(false); + this.BroadcastPlayerData(); + } + + public void OnTerritoryChange(object sender, ushort territoryId) => this.BroadcastPlayerData(); + + public void Dispose() { + // stop accepting new clients + this.tokenSource.Cancel(); + foreach (var client in this.clients.Values) { + Task.Run(async () => { + // tell clients we're shutting down + if (client.Handshake != null) { + try { + // time out after 5 seconds + client.Conn.SendTimeout = 5_000; + await SecretMessage.SendSecretMessage(client.Conn.GetStream(), client.Handshake.Keys.tx, ServerShutdown.Instance); + } catch (Exception) { } + } + // cancel threads for open clients + client.TokenSource.Cancel(); + }); + } + this.plugin.Functions.ReceiveFriendList -= this.OnReceiveFriendList; + } + } + + public class Client { + public bool Connected { get; set; } = false; + public TcpClient Conn { get; private set; } + public HandshakeInfo Handshake { get; set; } + public CancellationTokenSource TokenSource { get; } = new CancellationTokenSource(); + public Channel Queue { get; } = Channel.CreateUnbounded(); + + public Client(TcpClient conn) { + this.Conn = conn; + } + + public void Disconnect() { + this.Connected = false; + this.TokenSource.Cancel(); + this.Conn.Close(); + } + } + + internal static class TcpListenerExt { + public static async Task GetTcpClient(this TcpListener listener, CancellationTokenSource source) { + using (source.Token.Register(listener.Stop)) { + try { + var client = await listener.AcceptTcpClientAsync().ConfigureAwait(false); + return client; + } catch (ObjectDisposedException ex) { + // Token was canceled - swallow the exception and return null + if (source.Token.IsCancellationRequested) { + return null; + } + + throw ex; + } + } + } + } +} diff --git a/XIVChatPlugin/XIVChatPlugin.csproj b/XIVChatPlugin/XIVChatPlugin.csproj new file mode 100644 index 0000000..1086093 --- /dev/null +++ b/XIVChatPlugin/XIVChatPlugin.csproj @@ -0,0 +1,137 @@ + + + + + + + Debug + AnyCPU + {9AEB2B77-C127-4BA8-B9AE-FB3CA1649FDB} + Library + Properties + XIVChatPlugin + XIVChatPlugin + v4.8 + 512 + true + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + False + + + %appdata%\XIVLauncher\addon\Hooks\ImGui.NET.dll + False + + + %appdata%\XIVLauncher\addon\Hooks\ImGuiScene.dll + False + + + %appdata%\XIVLauncher\addon\Hooks\Lumina.dll + False + + + %appdata%\XIVLauncher\addon\Hooks\Lumina.Generated.dll + False + + + ..\packages\MessagePack.2.1.194\lib\netstandard2.0\MessagePack.dll + + + ..\packages\MessagePack.Annotations.2.1.194\lib\netstandard2.0\MessagePack.Annotations.dll + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\packages\Sodium.Core.1.2.3\lib\netstandard2.0\Sodium.Core.dll + + + + ..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll + + + + ..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Channels.4.7.1\lib\net461\System.Threading.Channels.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + False + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + + + + + + + + + + + + {6f426eab-15d4-48ac-80d2-2d2df2ce5ee0} + XIVChatCommon + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/XIVChatPlugin/XIVChatPlugin.json b/XIVChatPlugin/XIVChatPlugin.json new file mode 100644 index 0000000..022a872 --- /dev/null +++ b/XIVChatPlugin/XIVChatPlugin.json @@ -0,0 +1,9 @@ +{ + "Author": "ascclemens", + "Name": "XIVChat Server", + "Description": "Allows you to use various XIVChat clients to chat in FFXIV from anywhere!", + "InternalName": "XIVChatPlugin", + "AssemblyVersion": "1.0.0", + "ApplicableVersion": "any", + "DalamudApiLevel": 1 +} diff --git a/XIVChatPlugin/app.config b/XIVChatPlugin/app.config new file mode 100644 index 0000000..e3c9220 --- /dev/null +++ b/XIVChatPlugin/app.config @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/XIVChatPlugin/packages.config b/XIVChatPlugin/packages.config new file mode 100644 index 0000000..881c6d5 --- /dev/null +++ b/XIVChatPlugin/packages.config @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/logo-no-background.svg b/logo-no-background.svg new file mode 100644 index 0000000..6081f3c --- /dev/null +++ b/logo-no-background.svg @@ -0,0 +1,84 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + XV + | + + diff --git a/logo-no-outer-background.svg b/logo-no-outer-background.svg new file mode 100644 index 0000000..c566129 --- /dev/null +++ b/logo-no-outer-background.svg @@ -0,0 +1,122 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logo-plain.svg b/logo-plain.svg new file mode 100644 index 0000000..5d9a454 --- /dev/null +++ b/logo-plain.svg @@ -0,0 +1,83 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + XV + | + + diff --git a/logo.svg b/logo.svg new file mode 100644 index 0000000..d07795c --- /dev/null +++ b/logo.svg @@ -0,0 +1,121 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + XV + | + +