diff --git a/.github/generate_changelog.py b/.github/generate_changelog.py index b07100115..5e921fd6e 100644 --- a/.github/generate_changelog.py +++ b/.github/generate_changelog.py @@ -8,8 +8,7 @@ import re import sys import json import argparse -import os -from typing import List, Tuple, Optional, Dict, Any +from typing import List, Tuple, Optional def run_git_command(args: List[str]) -> str: @@ -31,14 +30,14 @@ def get_last_two_tags() -> Tuple[str, str]: """Get the latest two git tags.""" tags = run_git_command(["tag", "--sort=-version:refname"]) tag_list = [t for t in tags.split("\n") if t] - + # Filter out old tags that start with 'v' (old versioning scheme) tag_list = [t for t in tag_list if not t.startswith('v')] if len(tag_list) < 2: print("Error: Need at least 2 tags in the repository", file=sys.stderr) sys.exit(1) - + return tag_list[0], tag_list[1] @@ -56,144 +55,58 @@ def get_submodule_commit(submodule_path: str, tag: str) -> Optional[str]: return None -def get_repo_info() -> Tuple[str, str]: - """Get repository owner and name from git remote.""" - try: - remote_url = run_git_command(["config", "--get", "remote.origin.url"]) - - # Handle both HTTPS and SSH URLs - # SSH: git@github.com:owner/repo.git - # HTTPS: https://github.com/owner/repo.git - match = re.search(r'github\.com[:/](.+?)/(.+?)(?:\.git)?$', remote_url) - if match: - owner = match.group(1) - repo = match.group(2) - return owner, repo - else: - print("Error: Could not parse GitHub repository from remote URL", file=sys.stderr) - sys.exit(1) - except: - print("Error: Could not get git remote URL", file=sys.stderr) - sys.exit(1) - - -def get_commits_between_tags(tag1: str, tag2: str) -> List[str]: - """Get commit SHAs between two tags.""" +def get_commits_between_tags(tag1: str, tag2: str) -> List[Tuple[str, str]]: + """Get commits between two tags. Returns list of (message, author) tuples.""" log_output = run_git_command([ "log", f"{tag2}..{tag1}", - "--format=%H" + "--format=%s|%an|%h" ]) - - commits = [sha.strip() for sha in log_output.split("\n") if sha.strip()] + + commits = [] + for line in log_output.split("\n"): + if "|" in line: + message, author, sha = line.split("|", 2) + commits.append((message.strip(), author.strip(), sha.strip())) + return commits -def get_pr_for_commit(commit_sha: str, owner: str, repo: str, token: str) -> Optional[Dict[str, Any]]: - """Get PR information for a commit using GitHub API.""" - try: - import requests - except ImportError: - print("Error: requests library is required. Install it with: pip install requests", file=sys.stderr) - sys.exit(1) - - headers = { - "Accept": "application/vnd.github+json", - "X-GitHub-Api-Version": "2022-11-28" - } - - if token: - headers["Authorization"] = f"Bearer {token}" - - url = f"https://api.github.com/repos/{owner}/{repo}/commits/{commit_sha}/pulls" - - try: - response = requests.get(url, headers=headers) - response.raise_for_status() - prs = response.json() - - if prs and len(prs) > 0: - # Return the first PR (most relevant one) - pr = prs[0] - return { - "number": pr["number"], - "title": pr["title"], - "author": pr["user"]["login"], - "url": pr["html_url"] - } - except requests.exceptions.HTTPError as e: - if e.response.status_code == 404: - # Commit might not be associated with a PR - return None - elif e.response.status_code == 403: - print("Warning: GitHub API rate limit exceeded. Consider providing a token.", file=sys.stderr) - return None - else: - print(f"Warning: Failed to fetch PR for commit {commit_sha[:7]}: {e}", file=sys.stderr) - return None - except Exception as e: - print(f"Warning: Error fetching PR for commit {commit_sha[:7]}: {e}", file=sys.stderr) - return None - - return None - - -def get_prs_between_tags(tag1: str, tag2: str, owner: str, repo: str, token: str) -> List[Dict[str, Any]]: - """Get PRs between two tags using GitHub API.""" - commits = get_commits_between_tags(tag1, tag2) - print(f"Found {len(commits)} commits, fetching PR information...") - - prs = [] - seen_pr_numbers = set() - - for i, commit_sha in enumerate(commits, 1): - if i % 10 == 0: - print(f"Progress: {i}/{len(commits)} commits processed...") - - pr_info = get_pr_for_commit(commit_sha, owner, repo, token) - if pr_info and pr_info["number"] not in seen_pr_numbers: - seen_pr_numbers.add(pr_info["number"]) - prs.append(pr_info) - - return prs - - -def filter_prs(prs: List[Dict[str, Any]], ignore_patterns: List[str]) -> List[Dict[str, Any]]: - """Filter out PRs matching any of the ignore patterns.""" +def filter_commits(commits: List[Tuple[str, str]], ignore_patterns: List[str]) -> List[Tuple[str, str]]: + """Filter out commits matching any of the ignore patterns.""" compiled_patterns = [re.compile(pattern) for pattern in ignore_patterns] - + filtered = [] - for pr in prs: - if not any(pattern.search(pr["title"]) for pattern in compiled_patterns): - filtered.append(pr) - + for message, author, sha in commits: + if not any(pattern.search(message) for pattern in compiled_patterns): + filtered.append((message, author, sha)) + return filtered -def generate_changelog(version: str, prev_version: str, prs: List[Dict[str, Any]], - cs_commit_new: Optional[str], cs_commit_old: Optional[str], - owner: str, repo: str) -> str: +def generate_changelog(version: str, prev_version: str, commits: List[Tuple[str, str]], + cs_commit_new: Optional[str], cs_commit_old: Optional[str]) -> str: """Generate markdown changelog.""" # Calculate statistics - pr_count = len(prs) - unique_authors = len(set(pr["author"] for pr in prs)) - + commit_count = len(commits) + unique_authors = len(set(author for _, author, _ in commits)) + changelog = f"# Dalamud Release v{version}\n\n" changelog += f"We just released Dalamud v{version}, which should be available to users within a few minutes. " - changelog += f"This release includes **{pr_count} PR{'s' if pr_count != 1 else ''} from {unique_authors} contributor{'s' if unique_authors != 1 else ''}**.\n" - changelog += f"[Click here]() to see all Dalamud changes.\n\n" - + changelog += f"This release includes **{commit_count} commit{'s' if commit_count != 1 else ''} from {unique_authors} contributor{'s' if unique_authors != 1 else ''}**.\n" + changelog += f"[Click here]() to see all Dalamud changes.\n\n" + if cs_commit_new and cs_commit_old and cs_commit_new != cs_commit_old: changelog += f"It ships with an updated **FFXIVClientStructs [`{cs_commit_new[:7]}`]()**.\n" changelog += f"[Click here]() to see all CS changes.\n" elif cs_commit_new: changelog += f"It ships with **FFXIVClientStructs [`{cs_commit_new[:7]}`]()**.\n" - + changelog += "## Dalamud Changes\n\n" - - for pr in prs: - changelog += f"* {pr['title']} ([#**{pr['number']}**](<{pr['url']}>) by **{pr['author']}**)\n" - + + for message, author, sha in commits: + changelog += f"* {message} (by **{author}** as [`{sha}`]())\n" + return changelog @@ -204,9 +117,9 @@ def post_to_discord(webhook_url: str, content: str, version: str) -> None: except ImportError: print("Error: requests library is required. Install it with: pip install requests", file=sys.stderr) sys.exit(1) - + filename = f"changelog-v{version}.md" - + # Prepare the payload data = { "content": f"Dalamud v{version} has been released!", @@ -217,13 +130,13 @@ def post_to_discord(webhook_url: str, content: str, version: str) -> None: } ] } - + # Prepare the files files = { "payload_json": (None, json.dumps(data)), "files[0]": (filename, content.encode('utf-8'), 'text/markdown') } - + try: result = requests.post(webhook_url, files=files) result.raise_for_status() @@ -245,64 +158,54 @@ def main(): required=True, help="Discord webhook URL" ) - parser.add_argument( - "--github-token", - default=os.environ.get("GITHUB_TOKEN"), - help="GitHub API token (or set GITHUB_TOKEN env var). Increases rate limit." - ) parser.add_argument( "--ignore", action="append", default=[], - help="Regex patterns to ignore PRs (can be specified multiple times)" + help="Regex patterns to ignore commits (can be specified multiple times)" ) parser.add_argument( "--submodule-path", default="lib/FFXIVClientStructs", help="Path to the FFXIVClientStructs submodule (default: lib/FFXIVClientStructs)" ) - + args = parser.parse_args() - - # Get repository info - owner, repo = get_repo_info() - print(f"Repository: {owner}/{repo}") - + # Get the last two tags latest_tag, previous_tag = get_last_two_tags() print(f"Generating changelog between {previous_tag} and {latest_tag}") - + # Get submodule commits at both tags cs_commit_new = get_submodule_commit(args.submodule_path, latest_tag) cs_commit_old = get_submodule_commit(args.submodule_path, previous_tag) - + if cs_commit_new: print(f"FFXIVClientStructs commit (new): {cs_commit_new[:7]}") if cs_commit_old: print(f"FFXIVClientStructs commit (old): {cs_commit_old[:7]}") - - # Get PRs between tags - prs = get_prs_between_tags(latest_tag, previous_tag, owner, repo, args.github_token) - prs.reverse() - print(f"Found {len(prs)} PRs") - - # Filter PRs - filtered_prs = filter_prs(prs, args.ignore) - print(f"After filtering: {len(filtered_prs)} PRs") - + + # Get commits between tags + commits = get_commits_between_tags(latest_tag, previous_tag) + print(f"Found {len(commits)} commits") + + # Filter commits + filtered_commits = filter_commits(commits, args.ignore) + print(f"After filtering: {len(filtered_commits)} commits") + # Generate changelog - changelog = generate_changelog(latest_tag, previous_tag, filtered_prs, - cs_commit_new, cs_commit_old, owner, repo) - + changelog = generate_changelog(latest_tag, previous_tag, filtered_commits, + cs_commit_new, cs_commit_old) + print("\n" + "="*50) print("Generated Changelog:") print("="*50) print(changelog) print("="*50 + "\n") - + # Post to Discord post_to_discord(args.webhook_url, changelog, latest_tag) if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/.github/workflows/generate-changelog.yml b/.github/workflows/generate-changelog.yml index e62d5f37c..5fed3b1eb 100644 --- a/.github/workflows/generate-changelog.yml +++ b/.github/workflows/generate-changelog.yml @@ -6,43 +6,41 @@ on: tags: - '*' -permissions: read-all - jobs: generate-changelog: runs-on: ubuntu-latest - + steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 # Fetch all history and tags submodules: true # Fetch submodules - + - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.14' - + - name: Install dependencies run: | python -m pip install --upgrade pip pip install requests - + - name: Generate and post changelog - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GIT_TERMINAL_PROMPT: 0 run: | python .github/generate_changelog.py \ --webhook-url "${{ secrets.DISCORD_CHANGELOG_WEBHOOK_URL }}" \ - --ignore "Update ClientStructs" \ - --ignore "^build:" - + --ignore "^Merge" \ + --ignore "^build:" \ + --ignore "^docs:" + env: + GIT_TERMINAL_PROMPT: 0 + - name: Upload changelog as artifact if: always() uses: actions/upload-artifact@v4 with: name: changelog path: changelog-*.md - if-no-files-found: ignore + if-no-files-found: ignore \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 209ed90de..be44afacc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,7 +3,7 @@ on: [push, pull_request, workflow_dispatch] concurrency: group: build_dalamud_${{ github.ref_name }} - cancel-in-progress: false + cancel-in-progress: true jobs: build: @@ -23,7 +23,7 @@ jobs: uses: microsoft/setup-msbuild@v1.0.2 - uses: actions/setup-dotnet@v3 with: - dotnet-version: '10.0.100' + dotnet-version: '9.0.200' - name: Define VERSION run: | $env:COMMIT = $env:GITHUB_SHA.Substring(0, 7) diff --git a/.github/workflows/rollup.yml b/.github/workflows/rollup.yml index f4e013258..8fe049ad7 100644 --- a/.github/workflows/rollup.yml +++ b/.github/workflows/rollup.yml @@ -1,8 +1,8 @@ name: Rollup changes to next version on: -# push: -# branches: -# - master + push: + branches: + - master workflow_dispatch: jobs: diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index 6ffb3bb01..8331affcc 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -1,57 +1,19 @@ { "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Build Schema", + "$ref": "#/definitions/build", "definitions": { - "Host": { - "type": "string", - "enum": [ - "AppVeyor", - "AzurePipelines", - "Bamboo", - "Bitbucket", - "Bitrise", - "GitHubActions", - "GitLab", - "Jenkins", - "Rider", - "SpaceAutomation", - "TeamCity", - "Terminal", - "TravisCI", - "VisualStudio", - "VSCode" - ] - }, - "ExecutableTarget": { - "type": "string", - "enum": [ - "CI", - "Clean", - "Compile", - "CompileCImGui", - "CompileCImGuizmo", - "CompileCImPlot", - "CompileDalamud", - "CompileDalamudBoot", - "CompileDalamudCrashHandler", - "CompileImGuiNatives", - "CompileInjector", - "Restore", - "SetCILogging", - "Test" - ] - }, - "Verbosity": { - "type": "string", - "description": "", - "enum": [ - "Verbose", - "Normal", - "Minimal", - "Quiet" - ] - }, - "NukeBuild": { + "build": { + "type": "object", "properties": { + "Configuration": { + "type": "string", + "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)", + "enum": [ + "Debug", + "Release" + ] + }, "Continue": { "type": "boolean", "description": "Indicates to continue a previously failed build attempt" @@ -61,8 +23,29 @@ "description": "Shows the help text for this build assembly" }, "Host": { + "type": "string", "description": "Host for execution. Default is 'automatic'", - "$ref": "#/definitions/Host" + "enum": [ + "AppVeyor", + "AzurePipelines", + "Bamboo", + "Bitbucket", + "Bitrise", + "GitHubActions", + "GitLab", + "Jenkins", + "Rider", + "SpaceAutomation", + "TeamCity", + "Terminal", + "TravisCI", + "VisualStudio", + "VSCode" + ] + }, + "IsDocsBuild": { + "type": "boolean", + "description": "Whether we are building for documentation - emits generated files" }, "NoLogo": { "type": "boolean", @@ -91,46 +74,65 @@ "type": "array", "description": "List of targets to be skipped. Empty list skips all dependencies", "items": { - "$ref": "#/definitions/ExecutableTarget" + "type": "string", + "enum": [ + "CI", + "Clean", + "Compile", + "CompileCImGui", + "CompileCImGuizmo", + "CompileCImPlot", + "CompileDalamud", + "CompileDalamudBoot", + "CompileDalamudCrashHandler", + "CompileImGuiNatives", + "CompileInjector", + "CompileInjectorBoot", + "Restore", + "SetCILogging", + "Test" + ] } }, + "Solution": { + "type": "string", + "description": "Path to a solution file that is automatically loaded" + }, "Target": { "type": "array", "description": "List of targets to be invoked. Default is '{default_target}'", "items": { - "$ref": "#/definitions/ExecutableTarget" + "type": "string", + "enum": [ + "CI", + "Clean", + "Compile", + "CompileCImGui", + "CompileCImGuizmo", + "CompileCImPlot", + "CompileDalamud", + "CompileDalamudBoot", + "CompileDalamudCrashHandler", + "CompileImGuiNatives", + "CompileInjector", + "CompileInjectorBoot", + "Restore", + "SetCILogging", + "Test" + ] } }, "Verbosity": { + "type": "string", "description": "Logging verbosity during build execution. Default is 'Normal'", - "$ref": "#/definitions/Verbosity" - } - } - } - }, - "allOf": [ - { - "properties": { - "Configuration": { - "type": "string", - "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)", "enum": [ - "Debug", - "Release" + "Minimal", + "Normal", + "Quiet", + "Verbose" ] - }, - "IsDocsBuild": { - "type": "boolean", - "description": "Whether we are building for documentation - emits generated files" - }, - "Solution": { - "type": "string", - "description": "Path to a solution file that is automatically loaded" } } - }, - { - "$ref": "#/definitions/NukeBuild" } - ] -} + } +} \ No newline at end of file diff --git a/Dalamud.Boot/DalamudStartInfo.cpp b/Dalamud.Boot/DalamudStartInfo.cpp index 9c8fd9721..5be8f97d0 100644 --- a/Dalamud.Boot/DalamudStartInfo.cpp +++ b/Dalamud.Boot/DalamudStartInfo.cpp @@ -108,11 +108,6 @@ void from_json(const nlohmann::json& json, DalamudStartInfo& config) { config.LogName = json.value("LogName", config.LogName); config.PluginDirectory = json.value("PluginDirectory", config.PluginDirectory); config.AssetDirectory = json.value("AssetDirectory", config.AssetDirectory); - - if (json.contains("TempDirectory") && !json["TempDirectory"].is_null()) { - config.TempDirectory = json.value("TempDirectory", config.TempDirectory); - } - config.Language = json.value("Language", config.Language); config.Platform = json.value("Platform", config.Platform); config.GameVersion = json.value("GameVersion", config.GameVersion); diff --git a/Dalamud.Boot/DalamudStartInfo.h b/Dalamud.Boot/DalamudStartInfo.h index 308dcab7d..0eeaddeed 100644 --- a/Dalamud.Boot/DalamudStartInfo.h +++ b/Dalamud.Boot/DalamudStartInfo.h @@ -44,7 +44,6 @@ struct DalamudStartInfo { std::string ConfigurationPath; std::string LogPath; std::string LogName; - std::string TempDirectory; std::string PluginDirectory; std::string AssetDirectory; ClientLanguage Language = ClientLanguage::English; diff --git a/Dalamud.Boot/crashhandler_shared.h b/Dalamud.Boot/crashhandler_shared.h index 0308306ce..8d93e4460 100644 --- a/Dalamud.Boot/crashhandler_shared.h +++ b/Dalamud.Boot/crashhandler_shared.h @@ -6,8 +6,6 @@ #define WIN32_LEAN_AND_MEAN #include -#define CUSTOM_EXCEPTION_EXTERNAL_EVENT 0x12345679 - struct exception_info { LPEXCEPTION_POINTERS pExceptionPointers; diff --git a/Dalamud.Boot/dllmain.cpp b/Dalamud.Boot/dllmain.cpp index 687089f82..80a16f89a 100644 --- a/Dalamud.Boot/dllmain.cpp +++ b/Dalamud.Boot/dllmain.cpp @@ -331,51 +331,6 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) { logging::I("VEH was disabled manually"); } - // ============================== CLR Reporting =================================== // - - // This is pretty horrible - CLR just doesn't provide a way for us to handle these events, and the API for it - // was pushed back to .NET 11, so we have to hook ReportEventW and catch them ourselves for now. - // Ideally all of this will go away once they get to it. - static std::shared_ptr> s_report_event_hook; - s_report_event_hook = std::make_shared>( - "advapi32.dll!ReportEventW (global import, hook_clr_report_event)", L"advapi32.dll", "ReportEventW"); - s_report_event_hook->set_detour([hook = s_report_event_hook.get()]( - HANDLE hEventLog, - WORD wType, - WORD wCategory, - DWORD dwEventID, - PSID lpUserSid, - WORD wNumStrings, - DWORD dwDataSize, - LPCWSTR* lpStrings, - LPVOID lpRawData)-> BOOL { - - // Check for CLR Error Event IDs - // https://github.com/dotnet/runtime/blob/v10.0.0/src/coreclr/vm/eventreporter.cpp#L370 - if (dwEventID != 1026 && // ERT_UnhandledException: The process was terminated due to an unhandled exception - dwEventID != 1025 && // ERT_ManagedFailFast: The application requested process termination through System.Environment.FailFast - dwEventID != 1023 && // ERT_UnmanagedFailFast: The process was terminated due to an internal error in the .NET Runtime - dwEventID != 1027 && // ERT_StackOverflow: The process was terminated due to a stack overflow - dwEventID != 1028) // ERT_CodeContractFailed: The application encountered a bug. A managed code contract (precondition, postcondition, object invariant, or assert) failed - { - return hook->call_original(hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize, lpStrings, lpRawData); - } - - if (wNumStrings == 0 || lpStrings == nullptr) { - logging::W("ReportEventW called with no strings."); - return hook->call_original(hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize, lpStrings, lpRawData); - } - - // In most cases, DalamudCrashHandler will kill us now, so call original here to make sure we still write to the event log. - const BOOL original_ret = hook->call_original(hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize, lpStrings, lpRawData); - - const std::wstring error_details(lpStrings[0]); - veh::raise_external_event(error_details); - - return original_ret; - }); - logging::I("ReportEventW hook installed."); - // ============================== Dalamud ==================================== // if (static_cast(g_startInfo.BootWaitMessageBox) & static_cast(DalamudStartInfo::WaitMessageboxFlags::BeforeDalamudEntrypoint)) diff --git a/Dalamud.Boot/veh.cpp b/Dalamud.Boot/veh.cpp index c0a0b034a..b0ec1cefa 100644 --- a/Dalamud.Boot/veh.cpp +++ b/Dalamud.Boot/veh.cpp @@ -31,8 +31,6 @@ HANDLE g_crashhandler_process = nullptr; HANDLE g_crashhandler_event = nullptr; HANDLE g_crashhandler_pipe_write = nullptr; -wchar_t g_external_event_info[16384] = L""; - std::recursive_mutex g_exception_handler_mutex; std::chrono::time_point g_time_start; @@ -124,7 +122,6 @@ static DalamudExpected append_injector_launch_args(std::vector(g_startInfo.LogName) + L"\""); args.emplace_back(L"--dalamud-plugin-directory=\"" + unicode::convert(g_startInfo.PluginDirectory) + L"\""); args.emplace_back(L"--dalamud-asset-directory=\"" + unicode::convert(g_startInfo.AssetDirectory) + L"\""); - args.emplace_back(L"--dalamud-temp-directory=\"" + unicode::convert(g_startInfo.TempDirectory) + L"\""); args.emplace_back(std::format(L"--dalamud-client-language={}", static_cast(g_startInfo.Language))); args.emplace_back(std::format(L"--dalamud-delay-initialize={}", g_startInfo.DelayInitializeMs)); // NoLoadPlugins/NoLoadThirdPartyPlugins: supplied from DalamudCrashHandler @@ -193,11 +190,7 @@ LONG exception_handler(EXCEPTION_POINTERS* ex) DuplicateHandle(GetCurrentProcess(), g_crashhandler_event, g_crashhandler_process, &exinfo.hEventHandle, 0, TRUE, DUPLICATE_SAME_ACCESS); std::wstring stackTrace; - if (ex->ExceptionRecord->ExceptionCode == CUSTOM_EXCEPTION_EXTERNAL_EVENT) - { - stackTrace = std::wstring(g_external_event_info); - } - else if (!g_clr) + if (!g_clr) { stackTrace = L"(no CLR stack trace available)"; } @@ -258,12 +251,6 @@ LONG WINAPI structured_exception_handler(EXCEPTION_POINTERS* ex) LONG WINAPI vectored_exception_handler(EXCEPTION_POINTERS* ex) { - // special case for CLR exceptions, always trigger crash handler - if (ex->ExceptionRecord->ExceptionCode == CUSTOM_EXCEPTION_EXTERNAL_EVENT) - { - return exception_handler(ex); - } - if (ex->ExceptionRecord->ExceptionCode == 0x12345678) { // pass @@ -281,7 +268,7 @@ LONG WINAPI vectored_exception_handler(EXCEPTION_POINTERS* ex) if (!is_ffxiv_address(L"ffxiv_dx11.exe", ex->ContextRecord->Rip) && !is_ffxiv_address(L"cimgui.dll", ex->ContextRecord->Rip)) - return EXCEPTION_CONTINUE_SEARCH; + return EXCEPTION_CONTINUE_SEARCH; } return exception_handler(ex); @@ -310,7 +297,7 @@ bool veh::add_handler(bool doFullDump, const std::string& workingDirectory) if (HANDLE hReadPipeRaw, hWritePipeRaw; CreatePipe(&hReadPipeRaw, &hWritePipeRaw, nullptr, 65536)) { hWritePipe.emplace(hWritePipeRaw, &CloseHandle); - + if (HANDLE hReadPipeInheritableRaw; DuplicateHandle(GetCurrentProcess(), hReadPipeRaw, GetCurrentProcess(), &hReadPipeInheritableRaw, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) { hReadPipeInheritable.emplace(hReadPipeInheritableRaw, &CloseHandle); @@ -328,9 +315,9 @@ bool veh::add_handler(bool doFullDump, const std::string& workingDirectory) } // additional information - STARTUPINFOEXW siex{}; + STARTUPINFOEXW siex{}; PROCESS_INFORMATION pi{}; - + siex.StartupInfo.cb = sizeof siex; siex.StartupInfo.dwFlags = STARTF_USESHOWWINDOW; siex.StartupInfo.wShowWindow = g_startInfo.CrashHandlerShow ? SW_SHOW : SW_HIDE; @@ -398,7 +385,7 @@ bool veh::add_handler(bool doFullDump, const std::string& workingDirectory) argstr.push_back(L' '); } argstr.pop_back(); - + if (!handles.empty() && !UpdateProcThreadAttribute(siex.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &handles[0], std::span(handles).size_bytes(), nullptr, nullptr)) { logging::W("Failed to launch DalamudCrashHandler.exe: UpdateProcThreadAttribute error 0x{:x}", GetLastError()); @@ -413,7 +400,7 @@ bool veh::add_handler(bool doFullDump, const std::string& workingDirectory) TRUE, // Set handle inheritance to FALSE EXTENDED_STARTUPINFO_PRESENT, // lpStartupInfo actually points to a STARTUPINFOEX(W) nullptr, // Use parent's environment block - nullptr, // Use parent's starting directory + nullptr, // Use parent's starting directory &siex.StartupInfo, // Pointer to STARTUPINFO structure &pi // Pointer to PROCESS_INFORMATION structure (removed extra parentheses) )) @@ -429,7 +416,7 @@ bool veh::add_handler(bool doFullDump, const std::string& workingDirectory) } CloseHandle(pi.hThread); - + g_crashhandler_process = pi.hProcess; g_crashhandler_pipe_write = hWritePipe->release(); logging::I("Launched DalamudCrashHandler.exe: PID {}", pi.dwProcessId); @@ -447,16 +434,3 @@ bool veh::remove_handler() } return false; } - -void veh::raise_external_event(const std::wstring& info) -{ - const auto info_size = std::min(info.size(), std::size(g_external_event_info) - 1); - wcsncpy_s(g_external_event_info, info.c_str(), info_size); - RaiseException(CUSTOM_EXCEPTION_EXTERNAL_EVENT, 0, 0, nullptr); -} - -extern "C" __declspec(dllexport) void BootVehRaiseExternalEventW(LPCWSTR info) -{ - const std::wstring info_wstr(info); - veh::raise_external_event(info_wstr); -} diff --git a/Dalamud.Boot/veh.h b/Dalamud.Boot/veh.h index 2a02c374e..1905272ea 100644 --- a/Dalamud.Boot/veh.h +++ b/Dalamud.Boot/veh.h @@ -4,5 +4,4 @@ namespace veh { bool add_handler(bool doFullDump, const std::string& workingDirectory); bool remove_handler(); - void raise_external_event(const std::wstring& info); } diff --git a/Dalamud.Common/DalamudStartInfo.cs b/Dalamud.Common/DalamudStartInfo.cs index 8c66a85ba..a0d7f8b0b 100644 --- a/Dalamud.Common/DalamudStartInfo.cs +++ b/Dalamud.Common/DalamudStartInfo.cs @@ -34,12 +34,6 @@ public record DalamudStartInfo /// public string? ConfigurationPath { get; set; } - /// - /// Gets or sets the directory for temporary files. This directory needs to exist and be writable to the user. - /// It should also be predictable and easy for launchers to find. - /// - public string? TempDirectory { get; set; } - /// /// Gets or sets the path of the log files. /// diff --git a/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj b/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj new file mode 100644 index 000000000..7f8de3843 --- /dev/null +++ b/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj @@ -0,0 +1,111 @@ + + + + {8874326B-E755-4D13-90B4-59AB263A3E6B} + Dalamud_Injector_Boot + Debug + x64 + + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + 10.0 + Dalamud.Injector + + + + Application + true + v143 + false + Unicode + ..\bin\$(Configuration)\ + obj\$(Configuration)\ + + + + + Level3 + true + true + stdcpp23 + pch.h + ProgramDatabase + CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + + + Console + true + false + ..\lib\CoreCLR;%(AdditionalLibraryDirectories) + $(OutDir)$(TargetName).Boot.pdb + + + + + true + false + MultiThreadedDebugDLL + _DEBUG;%(PreprocessorDefinitions) + + + false + false + + + + + true + true + MultiThreadedDLL + NDEBUG;%(PreprocessorDefinitions) + + + true + true + + + + + + nethost.dll + PreserveNewest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj.filters b/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj.filters new file mode 100644 index 000000000..8f4372d89 --- /dev/null +++ b/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj.filters @@ -0,0 +1,67 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {4faac519-3a73-4b2b-96e7-fb597f02c0be} + ico;rc + + + + + Resource Files + + + + + Resource Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/Dalamud.Injector/dalamud.ico b/Dalamud.Injector.Boot/dalamud.ico similarity index 100% rename from Dalamud.Injector/dalamud.ico rename to Dalamud.Injector.Boot/dalamud.ico diff --git a/Dalamud.Injector.Boot/main.cpp b/Dalamud.Injector.Boot/main.cpp new file mode 100644 index 000000000..df4120009 --- /dev/null +++ b/Dalamud.Injector.Boot/main.cpp @@ -0,0 +1,48 @@ +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include "..\Dalamud.Boot\logging.h" +#include "..\lib\CoreCLR\CoreCLR.h" +#include "..\lib\CoreCLR\boot.h" + +int wmain(int argc, wchar_t** argv) +{ + // Take care: don't redirect stderr/out here, we need to write our pid to stdout for XL to read + //logging::start_file_logging("dalamud.injector.boot.log", false); + logging::I("Dalamud Injector, (c) 2021 XIVLauncher Contributors"); + logging::I("Built at : " __DATE__ "@" __TIME__); + + wchar_t _module_path[MAX_PATH]; + GetModuleFileNameW(NULL, _module_path, sizeof _module_path / 2); + std::filesystem::path fs_module_path(_module_path); + + std::wstring runtimeconfig_path = _wcsdup(fs_module_path.replace_filename(L"Dalamud.Injector.runtimeconfig.json").c_str()); + std::wstring module_path = _wcsdup(fs_module_path.replace_filename(L"Dalamud.Injector.dll").c_str()); + + // =========================================================================== // + + void* entrypoint_vfn; + const auto result = InitializeClrAndGetEntryPoint( + GetModuleHandleW(nullptr), + false, + runtimeconfig_path, + module_path, + L"Dalamud.Injector.EntryPoint, Dalamud.Injector", + L"Main", + L"Dalamud.Injector.EntryPoint+MainDelegate, Dalamud.Injector", + &entrypoint_vfn); + + if (FAILED(result)) + return result; + + typedef int (CORECLR_DELEGATE_CALLTYPE* custom_component_entry_point_fn)(int, wchar_t**); + custom_component_entry_point_fn entrypoint_fn = reinterpret_cast(entrypoint_vfn); + + logging::I("Running Dalamud Injector..."); + const auto ret = entrypoint_fn(argc, argv); + logging::I("Done!"); + + return ret; +} diff --git a/Dalamud.Injector.Boot/pch.h b/Dalamud.Injector.Boot/pch.h new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/Dalamud.Injector.Boot/pch.h @@ -0,0 +1 @@ +#pragma once diff --git a/Dalamud.Injector.Boot/resources.rc b/Dalamud.Injector.Boot/resources.rc new file mode 100644 index 000000000..8369e82a1 --- /dev/null +++ b/Dalamud.Injector.Boot/resources.rc @@ -0,0 +1 @@ +MAINICON ICON "dalamud.ico" diff --git a/Dalamud.Injector/Dalamud.Injector.csproj b/Dalamud.Injector/Dalamud.Injector.csproj index a0b4f6451..4a55174a1 100644 --- a/Dalamud.Injector/Dalamud.Injector.csproj +++ b/Dalamud.Injector/Dalamud.Injector.csproj @@ -13,13 +13,12 @@ - Exe + Library ..\bin\$(Configuration)\ false false true false - dalamud.ico diff --git a/Dalamud.Injector/Program.cs b/Dalamud.Injector/EntryPoint.cs similarity index 98% rename from Dalamud.Injector/Program.cs rename to Dalamud.Injector/EntryPoint.cs index 13fcacef2..b876aa6ed 100644 --- a/Dalamud.Injector/Program.cs +++ b/Dalamud.Injector/EntryPoint.cs @@ -25,20 +25,34 @@ namespace Dalamud.Injector /// /// Entrypoint to the program. /// - public sealed class Program + public sealed class EntryPoint { + /// + /// A delegate used during initialization of the CLR from Dalamud.Injector.Boot. + /// + /// Count of arguments. + /// char** string arguments. + /// Return value (HRESULT). + public delegate int MainDelegate(int argc, IntPtr argvPtr); + /// /// Start the Dalamud injector. /// - /// Command line arguments. + /// Count of arguments. + /// byte** string arguments. /// Return value (HRESULT). - public static int Main(string[] argsArray) + public static int Main(int argc, IntPtr argvPtr) { try { - // API14 TODO: Refactor - var args = argsArray.ToList(); - args.Insert(0, Assembly.GetExecutingAssembly().Location); + List args = new(argc); + + unsafe + { + var argv = (IntPtr*)argvPtr; + for (var i = 0; i < argc; i++) + args.Add(Marshal.PtrToStringUni(argv[i])); + } Init(args); args.Remove("-v"); // Remove "verbose" flag @@ -291,7 +305,6 @@ namespace Dalamud.Injector var configurationPath = startInfo.ConfigurationPath; var pluginDirectory = startInfo.PluginDirectory; var assetDirectory = startInfo.AssetDirectory; - var tempDirectory = startInfo.TempDirectory; var delayInitializeMs = startInfo.DelayInitializeMs; var logName = startInfo.LogName; var logPath = startInfo.LogPath; @@ -322,10 +335,6 @@ namespace Dalamud.Injector { assetDirectory = args[i][key.Length..]; } - else if (args[i].StartsWith(key = "--dalamud-temp-directory=")) - { - tempDirectory = args[i][key.Length..]; - } else if (args[i].StartsWith(key = "--dalamud-delay-initialize=")) { delayInitializeMs = int.Parse(args[i][key.Length..]); @@ -438,7 +447,6 @@ namespace Dalamud.Injector startInfo.ConfigurationPath = configurationPath; startInfo.PluginDirectory = pluginDirectory; startInfo.AssetDirectory = assetDirectory; - startInfo.TempDirectory = tempDirectory; startInfo.Language = clientLanguage; startInfo.Platform = platform; startInfo.DelayInitializeMs = delayInitializeMs; diff --git a/Dalamud.sln b/Dalamud.sln index 758253b9c..c3af00f44 100644 --- a/Dalamud.sln +++ b/Dalamud.sln @@ -1,4 +1,4 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32319.34 MinimumVisualStudioVersion = 10.0.40219.1 @@ -7,6 +7,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig .gitignore = .gitignore tools\BannedSymbols.txt = tools\BannedSymbols.txt + targets\Dalamud.Plugin.Bootstrap.targets = targets\Dalamud.Plugin.Bootstrap.targets + targets\Dalamud.Plugin.targets = targets\Dalamud.Plugin.targets tools\dalamud.ruleset = tools\dalamud.ruleset Directory.Build.props = Directory.Build.props Directory.Packages.props = Directory.Packages.props @@ -25,6 +27,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dalamud.Boot", "Dalamud.Boo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.Injector", "Dalamud.Injector\Dalamud.Injector.csproj", "{5B832F73-5F54-4ADC-870F-D0095EF72C9A}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dalamud.Injector.Boot", "Dalamud.Injector.Boot\Dalamud.Injector.Boot.vcxproj", "{8874326B-E755-4D13-90B4-59AB263A3E6B}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.Test", "Dalamud.Test\Dalamud.Test.csproj", "{C8004563-1806-4329-844F-0EF6274291FC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dependencies", "Dependencies", "{E15BDA6D-E881-4482-94BA-BE5527E917FF}" @@ -45,6 +49,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InteropGenerator", "lib\FFX EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InteropGenerator.Runtime", "lib\FFXIVClientStructs\InteropGenerator.Runtime\InteropGenerator.Runtime.csproj", "{A6AA1C3F-9470-4922-9D3F-D4549657AB22}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Injector", "Injector", "{19775C83-7117-4A5F-AA00-18889F46A490}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utilities", "Utilities", "{8F079208-C227-4D96-9427-2BEBE0003944}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cimgui", "external\cimgui\cimgui.vcxproj", "{8430077C-F736-4246-A052-8EA1CECE844E}" @@ -75,17 +81,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lumina.Excel.Generator", "l EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lumina.Excel", "lib\Lumina.Excel\src\Lumina.Excel\Lumina.Excel.csproj", "{88FB719B-EB41-73C5-8D25-C03E0C69904F}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source Generators", "Source Generators", "{50BEC23B-FFFD-427B-A95D-27E1D1958FFF}" - ProjectSection(SolutionItems) = preProject - generators\Directory.Build.props = generators\Directory.Build.props - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.EnumGenerator", "generators\Dalamud.EnumGenerator\Dalamud.EnumGenerator\Dalamud.EnumGenerator.csproj", "{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.EnumGenerator.Sample", "generators\Dalamud.EnumGenerator\Dalamud.EnumGenerator.Sample\Dalamud.EnumGenerator.Sample.csproj", "{8CDAEB2D-5022-450A-A97F-181C6270185F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.EnumGenerator.Tests", "generators\Dalamud.EnumGenerator\Dalamud.EnumGenerator.Tests\Dalamud.EnumGenerator.Tests.csproj", "{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -108,6 +103,10 @@ Global {5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Debug|Any CPU.Build.0 = Debug|x64 {5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|Any CPU.ActiveCfg = Release|x64 {5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|Any CPU.Build.0 = Release|x64 + {8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|Any CPU.ActiveCfg = Debug|x64 + {8874326B-E755-4D13-90B4-59AB263A3E6B}.Debug|Any CPU.Build.0 = Debug|x64 + {8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|Any CPU.ActiveCfg = Release|x64 + {8874326B-E755-4D13-90B4-59AB263A3E6B}.Release|Any CPU.Build.0 = Release|x64 {C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.ActiveCfg = Debug|x64 {C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.Build.0 = Debug|x64 {C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.ActiveCfg = Release|x64 @@ -184,23 +183,13 @@ Global {88FB719B-EB41-73C5-8D25-C03E0C69904F}.Debug|Any CPU.Build.0 = Debug|Any CPU {88FB719B-EB41-73C5-8D25-C03E0C69904F}.Release|Any CPU.ActiveCfg = Release|Any CPU {88FB719B-EB41-73C5-8D25-C03E0C69904F}.Release|Any CPU.Build.0 = Release|Any CPU - {27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Release|Any CPU.Build.0 = Release|Any CPU - {8CDAEB2D-5022-450A-A97F-181C6270185F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8CDAEB2D-5022-450A-A97F-181C6270185F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8CDAEB2D-5022-450A-A97F-181C6270185F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8CDAEB2D-5022-450A-A97F-181C6270185F}.Release|Any CPU.Build.0 = Release|Any CPU - {F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {5B832F73-5F54-4ADC-870F-D0095EF72C9A} = {19775C83-7117-4A5F-AA00-18889F46A490} + {8874326B-E755-4D13-90B4-59AB263A3E6B} = {19775C83-7117-4A5F-AA00-18889F46A490} {4AFDB34A-7467-4D41-B067-53BC4101D9D0} = {8F079208-C227-4D96-9427-2BEBE0003944} {C9B87BD7-AF49-41C3-91F1-D550ADEB7833} = {8BBACF2D-7AB8-4610-A115-0E363D35C291} {E0D51896-604F-4B40-8CFE-51941607B3A1} = {8BBACF2D-7AB8-4610-A115-0E363D35C291} @@ -220,9 +209,6 @@ Global {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {E15BDA6D-E881-4482-94BA-BE5527E917FF} {5A44DF0C-C9DA-940F-4D6B-4A11D13AEA3D} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {88FB719B-EB41-73C5-8D25-C03E0C69904F} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} - {27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8} = {50BEC23B-FFFD-427B-A95D-27E1D1958FFF} - {8CDAEB2D-5022-450A-A97F-181C6270185F} = {50BEC23B-FFFD-427B-A95D-27E1D1958FFF} - {F5D92D2D-D36F-4471-B657-8B9AA6C98AD6} = {50BEC23B-FFFD-427B-A95D-27E1D1958FFF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {79B65AC9-C940-410E-AB61-7EA7E12C7599} diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs index 7e93d2863..da0d7c2c6 100644 --- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs +++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Dalamud.Game.Text; using Dalamud.Interface; using Dalamud.Interface.FontIdentifier; +using Dalamud.Interface.ImGuiNotification.Internal; using Dalamud.Interface.Internal; using Dalamud.Interface.Internal.ReShadeHandling; using Dalamud.Interface.Style; @@ -19,12 +20,9 @@ using Dalamud.Plugin.Internal.AutoUpdate; using Dalamud.Plugin.Internal.Profiles; using Dalamud.Storage; using Dalamud.Utility; - using Newtonsoft.Json; - using Serilog; using Serilog.Events; - using Windows.Win32.UI.WindowsAndMessaging; namespace Dalamud.Configuration.Internal; @@ -93,7 +91,7 @@ internal sealed class DalamudConfiguration : IInternalDisposableService /// /// Gets or sets a dictionary of seen FTUE levels. /// - public Dictionary SeenFtueLevels { get; set; } = []; + public Dictionary SeenFtueLevels { get; set; } = new(); /// /// Gets or sets the last loaded Dalamud version. @@ -110,10 +108,15 @@ internal sealed class DalamudConfiguration : IInternalDisposableService /// public bool DoPluginTest { get; set; } = false; + /// + /// Gets or sets a key to opt into Dalamud staging builds. + /// + public string? DalamudBetaKey { get; set; } = null; + /// /// Gets or sets a list of custom repos. /// - public List ThirdRepoList { get; set; } = []; + public List ThirdRepoList { get; set; } = new(); /// /// Gets or sets a value indicating whether a disclaimer regarding third-party repos has been dismissed. @@ -123,12 +126,12 @@ internal sealed class DalamudConfiguration : IInternalDisposableService /// /// Gets or sets a list of hidden plugins. /// - public List HiddenPluginInternalName { get; set; } = []; + public List HiddenPluginInternalName { get; set; } = new(); /// /// Gets or sets a list of seen plugins. /// - public List SeenPluginInternalName { get; set; } = []; + public List SeenPluginInternalName { get; set; } = new(); /// /// Gets or sets a list of additional settings for devPlugins. The key is the absolute path @@ -136,14 +139,14 @@ internal sealed class DalamudConfiguration : IInternalDisposableService /// However by specifiying this value manually, you can add arbitrary files outside the normal /// file paths. /// - public Dictionary DevPluginSettings { get; set; } = []; + public Dictionary DevPluginSettings { get; set; } = new(); /// /// Gets or sets a list of additional locations that dev plugins should be loaded from. This can /// be either a DLL or folder, but should be the absolute path, or a path relative to the currently /// injected Dalamud instance. /// - public List DevPluginLoadLocations { get; set; } = []; + public List DevPluginLoadLocations { get; set; } = new(); /// /// Gets or sets the global UI scale. @@ -225,7 +228,7 @@ internal sealed class DalamudConfiguration : IInternalDisposableService /// /// Gets or sets a list representing the command history for the Dalamud Console. /// - public List LogCommandHistory { get; set; } = []; + public List LogCommandHistory { get; set; } = new(); /// /// Gets or sets a value indicating whether the dev bar should open at startup. @@ -275,6 +278,11 @@ internal sealed class DalamudConfiguration : IInternalDisposableService /// public bool IsResumeGameAfterPluginLoad { get; set; } = false; + /// + /// Gets or sets the kind of beta to download when matches the server value. + /// + public string? DalamudBetaKind { get; set; } + /// /// Gets or sets a value indicating whether any plugin should be loaded when the game is started. /// It is reset immediately when read. @@ -489,24 +497,6 @@ internal sealed class DalamudConfiguration : IInternalDisposableService /// public Vector2 NotificationAnchorPosition { get; set; } = new(1f, 1f); -#pragma warning disable SA1600 -#pragma warning disable SA1516 - // XLCore/XoM compatibility until they move it out - public string? DalamudBetaKey { get; set; } = null; - public string? DalamudBetaKind { get; set; } -#pragma warning restore SA1516 -#pragma warning restore SA1600 - - /// - /// Gets or sets a list of badge passwords used to unlock badges. - /// - public List UsedBadgePasswords { get; set; } = []; - - /// - /// Gets or sets a value indicating whether badges should be shown on the title screen. - /// - public bool ShowBadgesOnTitleScreen { get; set; } = true; - /// /// Load a configuration from the provided path. /// @@ -601,7 +591,7 @@ internal sealed class DalamudConfiguration : IInternalDisposableService { // https://source.chromium.org/chromium/chromium/src/+/main:ui/gfx/animation/animation_win.cc;l=29?q=ReducedMotion&ss=chromium var winAnimEnabled = 0; - bool success; + var success = false; unsafe { success = Windows.Win32.PInvoke.SystemParametersInfo( diff --git a/Dalamud/Configuration/Internal/DevPluginSettings.cs b/Dalamud/Configuration/Internal/DevPluginSettings.cs index 400834cf8..64327e658 100644 --- a/Dalamud/Configuration/Internal/DevPluginSettings.cs +++ b/Dalamud/Configuration/Internal/DevPluginSettings.cs @@ -31,5 +31,5 @@ internal sealed class DevPluginSettings /// /// Gets or sets a list of validation problems that have been dismissed by the user. /// - public List DismissedValidationProblems { get; set; } = []; + public List DismissedValidationProblems { get; set; } = new(); } diff --git a/Dalamud/Configuration/PluginConfigurations.cs b/Dalamud/Configuration/PluginConfigurations.cs index 7ce4697cb..fa2969d31 100644 --- a/Dalamud/Configuration/PluginConfigurations.cs +++ b/Dalamud/Configuration/PluginConfigurations.cs @@ -11,7 +11,7 @@ namespace Dalamud.Configuration; /// /// Configuration to store settings for a dalamud plugin. /// -[Api15ToDo("Make this a service. We need to be able to dispose it reliably to write configs asynchronously. Maybe also let people write files with vfs.")] +[Api13ToDo("Make this a service. We need to be able to dispose it reliably to write configs asynchronously. Maybe also let people write files with vfs.")] public sealed class PluginConfigurations { private readonly DirectoryInfo configDirectory; diff --git a/Dalamud/Console/ConsoleManager.cs b/Dalamud/Console/ConsoleManager.cs index ba1c38b16..377bac208 100644 --- a/Dalamud/Console/ConsoleManager.cs +++ b/Dalamud/Console/ConsoleManager.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; @@ -17,9 +17,9 @@ namespace Dalamud.Console; [ServiceManager.BlockingEarlyLoadedService("Console is needed by other blocking early loaded services.")] internal partial class ConsoleManager : IServiceType { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("CON"); - private Dictionary entries = []; + private Dictionary entries = new(); /// /// Initializes a new instance of the class. @@ -99,7 +99,10 @@ internal partial class ConsoleManager : IServiceType ArgumentNullException.ThrowIfNull(name); ArgumentNullException.ThrowIfNull(alias); - var target = this.FindEntry(name) ?? throw new EntryNotFoundException(name); + var target = this.FindEntry(name); + if (target == null) + throw new EntryNotFoundException(name); + if (this.FindEntry(alias) != null) throw new InvalidOperationException($"Entry '{alias}' already exists."); @@ -343,7 +346,7 @@ internal partial class ConsoleManager : IServiceType private static class Traits { - public static void ThrowIfTIsNullableAndNull(T? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null) + public static void ThrowIfTIsNullableAndNull(T? argument, [CallerArgumentExpression("argument")] string? paramName = null) { if (argument == null && !typeof(T).IsValueType) throw new ArgumentNullException(paramName); diff --git a/Dalamud/Console/ConsoleManagerPluginScoped.cs b/Dalamud/Console/ConsoleManagerPluginScoped.cs index 8e7516429..41949c7d7 100644 --- a/Dalamud/Console/ConsoleManagerPluginScoped.cs +++ b/Dalamud/Console/ConsoleManagerPluginScoped.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; @@ -65,7 +65,7 @@ internal class ConsoleManagerPluginScoped : IConsole, IInternalDisposableService [ServiceManager.ServiceDependency] private readonly ConsoleManager console = Service.Get(); - private readonly List trackedEntries = []; + private readonly List trackedEntries = new(); /// /// Initializes a new instance of the class. diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index f80252ef9..a411883d5 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -9,14 +9,11 @@ using System.Threading.Tasks; using Dalamud.Common; using Dalamud.Configuration.Internal; using Dalamud.Game; -using Dalamud.Hooking.Internal.Verification; using Dalamud.Plugin.Internal; using Dalamud.Storage; using Dalamud.Utility; using Dalamud.Utility.Timing; - using Serilog; - using Windows.Win32.Foundation; using Windows.Win32.Security; @@ -76,11 +73,6 @@ internal sealed unsafe class Dalamud : IServiceType scanner, Localization.FromAssets(info.AssetDirectory!, configuration.LanguageOverride)); - using (Timings.Start("HookVerifier Init")) - { - HookVerifier.Initialize(scanner); - } - // Set up FFXIVClientStructs this.SetupClientStructsResolver(cacheDir); diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index 5a89f08ae..ce140b8c9 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -6,7 +6,7 @@ XIV Launcher addon framework - 14.0.2.2 + 13.0.0.11 $(DalamudVersion) $(DalamudVersion) $(DalamudVersion) @@ -65,6 +65,7 @@ + @@ -72,6 +73,8 @@ all + + @@ -82,20 +85,14 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + + - - - - - - - - - imgui-frag.hlsl.bytes @@ -125,8 +122,6 @@ PreserveNewest - - @@ -231,4 +226,9 @@ + + + + + diff --git a/Dalamud/DalamudAsset.cs b/Dalamud/DalamudAsset.cs index 9c0f247ee..27771116e 100644 --- a/Dalamud/DalamudAsset.cs +++ b/Dalamud/DalamudAsset.cs @@ -73,7 +73,7 @@ public enum DalamudAsset [DalamudAsset(DalamudAssetPurpose.TextureFromPng)] [DalamudAssetPath("UIRes", "troubleIcon.png")] TroubleIcon = 1006, - + /// /// : The plugin trouble icon overlay. /// @@ -124,13 +124,6 @@ public enum DalamudAsset [DalamudAssetPath("UIRes", "tsmShade.png")] TitleScreenMenuShade = 1013, - /// - /// : Atlas containing badges. - /// - [DalamudAsset(DalamudAssetPurpose.TextureFromPng)] - [DalamudAssetPath("UIRes", "badgeAtlas.png")] - BadgeAtlas = 1015, - /// /// : Noto Sans CJK JP Medium. /// @@ -158,7 +151,7 @@ public enum DalamudAsset /// : FontAwesome Free Solid. /// [DalamudAsset(DalamudAssetPurpose.Font)] - [DalamudAssetPath("UIRes", "FontAwesome710FreeSolid.otf")] + [DalamudAssetPath("UIRes", "FontAwesomeFreeSolid.otf")] FontAwesomeFreeSolid = 2003, /// diff --git a/Dalamud/Data/DataManager.cs b/Dalamud/Data/DataManager.cs index 11cb3a979..d017bf85a 100644 --- a/Dalamud/Data/DataManager.cs +++ b/Dalamud/Data/DataManager.cs @@ -8,13 +8,11 @@ using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; using Dalamud.Utility; using Dalamud.Utility.Timing; - using Lumina; using Lumina.Data; using Lumina.Excel; using Newtonsoft.Json; - using Serilog; namespace Dalamud.Data; @@ -43,7 +41,7 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager try { Log.Verbose("Starting data load..."); - + using (Timings.Start("Lumina Init")) { var luminaOptions = new LuminaOptions @@ -55,25 +53,12 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager DefaultExcelLanguage = this.Language.ToLumina(), }; - try + this.GameData = new( + Path.Combine(Path.GetDirectoryName(Environment.ProcessPath)!, "sqpack"), + luminaOptions) { - this.GameData = new( - Path.Combine(Path.GetDirectoryName(Environment.ProcessPath)!, "sqpack"), - luminaOptions) - { - StreamPool = new(), - }; - } - catch (Exception ex) - { - Log.Error(ex, "Lumina GameData init failed"); - Util.Fatal( - "Dalamud could not read required game data files. This likely means your game installation is corrupted or incomplete.\n\n" + - "Please repair your installation by right-clicking the login button in XIVLauncher and choosing \"Repair game files\".", - "Dalamud"); - - return; - } + StreamPool = new(), + }; Log.Information("Lumina is ready: {0}", this.GameData.DataPath); @@ -84,14 +69,9 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager var tsInfo = JsonConvert.DeserializeObject( dalamud.StartInfo.TroubleshootingPackData); - - // Don't fail for IndexIntegrityResult.Exception, since the check during launch has a very small timeout - // this.HasModifiedGameDataFiles = - // tsInfo?.IndexIntegrity is LauncherTroubleshootingInfo.IndexIntegrityResult.Failed; - - // TODO: Put above back when check in XL is fixed - this.HasModifiedGameDataFiles = false; - + this.HasModifiedGameDataFiles = + tsInfo?.IndexIntegrity is LauncherTroubleshootingInfo.IndexIntegrityResult.Failed or LauncherTroubleshootingInfo.IndexIntegrityResult.Exception; + if (this.HasModifiedGameDataFiles) Log.Verbose("Game data integrity check failed!\n{TsData}", dalamud.StartInfo.TroubleshootingPackData); } @@ -150,7 +130,7 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager #region Lumina Wrappers /// - public ExcelSheet GetExcelSheet(ClientLanguage? language = null, string? name = null) where T : struct, IExcelRow + public ExcelSheet GetExcelSheet(ClientLanguage? language = null, string? name = null) where T : struct, IExcelRow => this.Excel.GetSheet(language?.ToLumina(), name); /// @@ -158,7 +138,7 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager => this.Excel.GetSubrowSheet(language?.ToLumina(), name); /// - public FileResource? GetFile(string path) + public FileResource? GetFile(string path) => this.GetFile(path); /// @@ -181,7 +161,7 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager : Task.FromException(new FileNotFoundException("The file could not be found.")); /// - public bool FileExists(string path) + public bool FileExists(string path) => this.GameData.FileExists(path); #endregion diff --git a/Dalamud/Data/RsvResolver.cs b/Dalamud/Data/RsvResolver.cs index 6fd84356c..3f507ff1d 100644 --- a/Dalamud/Data/RsvResolver.cs +++ b/Dalamud/Data/RsvResolver.cs @@ -3,9 +3,7 @@ using System.Collections.Generic; using Dalamud.Hooking; using Dalamud.Logging.Internal; using Dalamud.Memory; - using FFXIVClientStructs.FFXIV.Client.LayoutEngine; - using Lumina.Text.ReadOnly; namespace Dalamud.Data; @@ -15,7 +13,7 @@ namespace Dalamud.Data; /// internal sealed unsafe class RsvResolver : IDisposable { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("RsvProvider"); private readonly Hook addRsvStringHook; diff --git a/Dalamud/EntryPoint.cs b/Dalamud/EntryPoint.cs index 7abebee3b..15077f3d8 100644 --- a/Dalamud/EntryPoint.cs +++ b/Dalamud/EntryPoint.cs @@ -1,6 +1,8 @@ using System.ComponentModel; using System.Diagnostics; using System.IO; +using System.Net; +using System.Reflection; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -14,13 +16,10 @@ using Dalamud.Plugin.Internal; using Dalamud.Storage; using Dalamud.Support; using Dalamud.Utility; - using Newtonsoft.Json; - using Serilog; using Serilog.Core; using Serilog.Events; - using Windows.Win32.Foundation; using Windows.Win32.UI.WindowsAndMessaging; @@ -193,8 +192,8 @@ public sealed class EntryPoint var dalamud = new Dalamud(info, fs, configuration, mainThreadContinueEvent); Log.Information("This is Dalamud - Core: {GitHash}, CS: {CsGitHash} [{CsVersion}]", - Versioning.GetScmVersion(), - Versioning.GetGitHashClientStructs(), + Util.GetScmVersion(), + Util.GetGitHashClientStructs(), FFXIVClientStructs.ThisAssembly.Git.Commits); dalamud.WaitForUnload(); @@ -264,7 +263,7 @@ public sealed class EntryPoint var symbolPath = Path.Combine(info.AssetDirectory, "UIRes", "pdb"); var searchPath = $".;{symbolPath}"; - var currentProcess = Windows.Win32.PInvoke.GetCurrentProcess(); + var currentProcess = Windows.Win32.PInvoke.GetCurrentProcess_SafeHandle(); // Remove any existing Symbol Handler and Init a new one with our search path added Windows.Win32.PInvoke.SymCleanup(currentProcess); @@ -293,6 +292,7 @@ public sealed class EntryPoint } var pluginInfo = string.Empty; + var supportText = ", please visit us on Discord for more help"; try { var pm = Service.GetNullable(); @@ -300,6 +300,9 @@ public sealed class EntryPoint if (plugin != null) { pluginInfo = $"Plugin that caused this:\n{plugin.Name}\n\nClick \"Yes\" and remove it.\n\n"; + + if (plugin.IsThirdParty) + supportText = string.Empty; } } catch @@ -307,18 +310,31 @@ public sealed class EntryPoint // ignored } - Log.CloseAndFlush(); + const MESSAGEBOX_STYLE flags = MESSAGEBOX_STYLE.MB_YESNO | MESSAGEBOX_STYLE.MB_ICONERROR | MESSAGEBOX_STYLE.MB_SYSTEMMODAL; + var result = Windows.Win32.PInvoke.MessageBox( + new HWND(Process.GetCurrentProcess().MainWindowHandle), + $"An internal error in a Dalamud plugin occurred.\nThe game must close.\n\n{ex.GetType().Name}\n{info}\n\n{pluginInfo}More information has been recorded separately{supportText}.\n\nDo you want to disable all plugins the next time you start the game?", + "Dalamud", + flags); - ErrorHandling.CrashWithContext($"{ex}\n\n{info}\n\n{pluginInfo}"); + if (result == MESSAGEBOX_RESULT.IDYES) + { + Log.Information("User chose to disable plugins on next launch..."); + var config = Service.Get(); + config.PluginSafeMode = true; + config.ForceSave(); + } + + Log.CloseAndFlush(); + Environment.Exit(-1); break; default: Log.Fatal("Unhandled SEH object on AppDomain: {Object}", args.ExceptionObject); Log.CloseAndFlush(); + Environment.Exit(-1); break; } - - Environment.Exit(-1); } private static void OnUnhandledExceptionStallDebug(object sender, UnhandledExceptionEventArgs args) diff --git a/Dalamud/EnumCloneMap.txt b/Dalamud/EnumCloneMap.txt deleted file mode 100644 index bbc3c1eda..000000000 --- a/Dalamud/EnumCloneMap.txt +++ /dev/null @@ -1,3 +0,0 @@ -# Format: Target.Full.TypeName = Source.Full.EnumTypeName -# Example: Generate a local enum MyGeneratedEnum in namespace Sample.Gen mapped to SourceEnums.SampleSourceEnum -Dalamud.Game.Agent.AgentId = FFXIVClientStructs.FFXIV.Client.UI.Agent.AgentId diff --git a/Dalamud/Game/Addon/AddonLifecyclePooledArgs.cs b/Dalamud/Game/Addon/AddonLifecyclePooledArgs.cs new file mode 100644 index 000000000..14def2036 --- /dev/null +++ b/Dalamud/Game/Addon/AddonLifecyclePooledArgs.cs @@ -0,0 +1,107 @@ +using System.Runtime.CompilerServices; +using System.Threading; + +using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; + +namespace Dalamud.Game.Addon; + +/// Argument pool for Addon Lifecycle services. +[ServiceManager.EarlyLoadedService] +internal sealed class AddonLifecyclePooledArgs : IServiceType +{ + private readonly AddonSetupArgs?[] addonSetupArgPool = new AddonSetupArgs?[64]; + private readonly AddonFinalizeArgs?[] addonFinalizeArgPool = new AddonFinalizeArgs?[64]; + private readonly AddonDrawArgs?[] addonDrawArgPool = new AddonDrawArgs?[64]; + private readonly AddonUpdateArgs?[] addonUpdateArgPool = new AddonUpdateArgs?[64]; + private readonly AddonRefreshArgs?[] addonRefreshArgPool = new AddonRefreshArgs?[64]; + private readonly AddonRequestedUpdateArgs?[] addonRequestedUpdateArgPool = new AddonRequestedUpdateArgs?[64]; + private readonly AddonReceiveEventArgs?[] addonReceiveEventArgPool = new AddonReceiveEventArgs?[64]; + + [ServiceManager.ServiceConstructor] + private AddonLifecyclePooledArgs() + { + } + + /// Rents an instance of an argument. + /// The rented instance. + /// The returner. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public PooledEntry Rent(out AddonSetupArgs arg) => new(out arg, this.addonSetupArgPool); + + /// Rents an instance of an argument. + /// The rented instance. + /// The returner. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public PooledEntry Rent(out AddonFinalizeArgs arg) => new(out arg, this.addonFinalizeArgPool); + + /// Rents an instance of an argument. + /// The rented instance. + /// The returner. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public PooledEntry Rent(out AddonDrawArgs arg) => new(out arg, this.addonDrawArgPool); + + /// Rents an instance of an argument. + /// The rented instance. + /// The returner. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public PooledEntry Rent(out AddonUpdateArgs arg) => new(out arg, this.addonUpdateArgPool); + + /// Rents an instance of an argument. + /// The rented instance. + /// The returner. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public PooledEntry Rent(out AddonRefreshArgs arg) => new(out arg, this.addonRefreshArgPool); + + /// Rents an instance of an argument. + /// The rented instance. + /// The returner. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public PooledEntry Rent(out AddonRequestedUpdateArgs arg) => + new(out arg, this.addonRequestedUpdateArgPool); + + /// Rents an instance of an argument. + /// The rented instance. + /// The returner. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public PooledEntry Rent(out AddonReceiveEventArgs arg) => + new(out arg, this.addonReceiveEventArgPool); + + /// Returns the object to the pool on dispose. + /// The type. + public readonly ref struct PooledEntry + where T : AddonArgs, new() + { + private readonly Span pool; + private readonly T obj; + + /// Initializes a new instance of the struct. + /// An instance of the argument. + /// The pool to rent from and return to. + public PooledEntry(out T arg, Span pool) + { + this.pool = pool; + foreach (ref var item in pool) + { + if (Interlocked.Exchange(ref item, null) is { } v) + { + this.obj = arg = v; + return; + } + } + + this.obj = arg = new(); + } + + /// Returns the item to the pool. + public void Dispose() + { + var tmp = this.obj; + foreach (ref var item in this.pool) + { + if (Interlocked.Exchange(ref item, tmp) is not { } tmp2) + return; + tmp = tmp2; + } + } + } +} diff --git a/Dalamud/Game/Addon/Events/AddonEventEntry.cs b/Dalamud/Game/Addon/Events/AddonEventEntry.cs index eca1903f2..30d0465dc 100644 --- a/Dalamud/Game/Addon/Events/AddonEventEntry.cs +++ b/Dalamud/Game/Addon/Events/AddonEventEntry.cs @@ -1,4 +1,5 @@ using Dalamud.Plugin.Services; +using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Component.GUI; diff --git a/Dalamud/Game/Addon/Events/AddonEventManager.cs b/Dalamud/Game/Addon/Events/AddonEventManager.cs index 17b70fcc2..945197e2b 100644 --- a/Dalamud/Game/Addon/Events/AddonEventManager.cs +++ b/Dalamud/Game/Addon/Events/AddonEventManager.cs @@ -9,6 +9,7 @@ using Dalamud.Logging.Internal; using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Component.GUI; namespace Dalamud.Game.Addon.Events; @@ -24,28 +25,32 @@ internal unsafe class AddonEventManager : IInternalDisposableService /// public static readonly Guid DalamudInternalKey = Guid.NewGuid(); - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("AddonEventManager"); [ServiceManager.ServiceDependency] private readonly AddonLifecycle addonLifecycle = Service.Get(); private readonly AddonLifecycleEventListener finalizeEventListener; - private readonly Hook onUpdateCursor; + private readonly AddonEventManagerAddressResolver address; + private readonly Hook onUpdateCursor; private readonly ConcurrentDictionary pluginEventControllers; - private AtkCursor.CursorType? cursorOverride; + private AddonCursorType? cursorOverride; [ServiceManager.ServiceConstructor] - private AddonEventManager() + private AddonEventManager(TargetSigScanner sigScanner) { + this.address = new AddonEventManagerAddressResolver(); + this.address.Setup(sigScanner); + this.pluginEventControllers = new ConcurrentDictionary(); this.pluginEventControllers.TryAdd(DalamudInternalKey, new PluginEventController()); this.cursorOverride = null; - this.onUpdateCursor = Hook.FromAddress(AtkUnitManager.Addresses.UpdateCursor.Value, this.UpdateCursorDetour); + this.onUpdateCursor = Hook.FromAddress(this.address.UpdateCursor, this.UpdateCursorDetour); this.finalizeEventListener = new AddonLifecycleEventListener(AddonEvent.PreFinalize, string.Empty, this.OnAddonFinalize); this.addonLifecycle.RegisterListener(this.finalizeEventListener); @@ -53,6 +58,8 @@ internal unsafe class AddonEventManager : IInternalDisposableService this.onUpdateCursor.Enable(); } + private delegate nint UpdateCursorDelegate(RaptureAtkModule* module); + /// void IInternalDisposableService.DisposeService() { @@ -110,7 +117,7 @@ internal unsafe class AddonEventManager : IInternalDisposableService /// Force the game cursor to be the specified cursor. /// /// Which cursor to use. - internal void SetCursor(AddonCursorType cursor) => this.cursorOverride = (AtkCursor.CursorType)cursor; + internal void SetCursor(AddonCursorType cursor) => this.cursorOverride = cursor; /// /// Un-forces the game cursor. @@ -161,7 +168,7 @@ internal unsafe class AddonEventManager : IInternalDisposableService } } - private void UpdateCursorDetour(AtkUnitManager* thisPtr) + private nint UpdateCursorDetour(RaptureAtkModule* module) { try { @@ -169,14 +176,13 @@ internal unsafe class AddonEventManager : IInternalDisposableService if (this.cursorOverride is not null && atkStage is not null) { - ref var atkCursor = ref atkStage->AtkCursor; - - if (atkCursor.Type != this.cursorOverride) + var cursor = (AddonCursorType)atkStage->AtkCursor.Type; + if (cursor != this.cursorOverride) { - atkCursor.SetCursorType((AtkCursor.CursorType)this.cursorOverride, 1); + AtkStage.Instance()->AtkCursor.SetCursorType((AtkCursor.CursorType)this.cursorOverride, 1); } - return; + return nint.Zero; } } catch (Exception e) @@ -184,7 +190,7 @@ internal unsafe class AddonEventManager : IInternalDisposableService Log.Error(e, "Exception in UpdateCursorDetour."); } - this.onUpdateCursor!.Original(thisPtr); + return this.onUpdateCursor!.Original(module); } } diff --git a/Dalamud/Game/Addon/Events/AddonEventManagerAddressResolver.cs b/Dalamud/Game/Addon/Events/AddonEventManagerAddressResolver.cs new file mode 100644 index 000000000..415e1b169 --- /dev/null +++ b/Dalamud/Game/Addon/Events/AddonEventManagerAddressResolver.cs @@ -0,0 +1,21 @@ +namespace Dalamud.Game.Addon.Events; + +/// +/// AddonEventManager memory address resolver. +/// +internal class AddonEventManagerAddressResolver : BaseAddressResolver +{ + /// + /// Gets the address of the AtkModule UpdateCursor method. + /// + public nint UpdateCursor { get; private set; } + + /// + /// Scan for and setup any configured address pointers. + /// + /// The signature scanner to facilitate setup. + protected override void Setup64Bit(ISigScanner scanner) + { + this.UpdateCursor = scanner.ScanText("48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 20 4C 8B F1 E8 ?? ?? ?? ?? 49 8B CE"); // unnamed in CS + } +} diff --git a/Dalamud/Game/Addon/Events/AddonEventType.cs b/Dalamud/Game/Addon/Events/AddonEventType.cs index 9e062a9d0..25beb13fc 100644 --- a/Dalamud/Game/Addon/Events/AddonEventType.cs +++ b/Dalamud/Game/Addon/Events/AddonEventType.cs @@ -61,11 +61,6 @@ public enum AddonEventType : byte /// InputBaseInputReceived = 15, - /// - /// Fired at the very beginning of AtkInputManager.HandleInput on AtkStage.ViewportEventManager. Used in LovmMiniMap. - /// - RawInputData = 16, - /// /// Focus Start. /// @@ -112,12 +107,7 @@ public enum AddonEventType : byte SliderReleased = 30, /// - /// AtkComponentList Button Press. - /// - ListButtonPress = 31, - - /// - /// AtkComponentList Roll Over. + /// AtkComponentList RollOver. /// ListItemRollOver = 33, @@ -136,31 +126,11 @@ public enum AddonEventType : byte /// ListItemDoubleClick = 36, - /// - /// AtkComponentList Highlight. - /// - ListItemHighlight = 37, - /// /// AtkComponentList Select. /// ListItemSelect = 38, - /// - /// AtkComponentList Pad Drag Drop Begin. - /// - ListItemPadDragDropBegin = 40, - - /// - /// AtkComponentList Pad Drag Drop End. - /// - ListItemPadDragDropEnd = 41, - - /// - /// AtkComponentList Pad Drag Drop Insert. - /// - ListItemPadDragDropInsert = 42, - /// /// AtkComponentDragDrop Begin. /// Sent on MouseDown over a draggable icon (will NOT send for a locked icon). @@ -172,22 +142,12 @@ public enum AddonEventType : byte /// DragDropEnd = 51, - /// - /// AtkComponentDragDrop Insert Attempt. - /// - DragDropInsertAttempt = 52, - /// /// AtkComponentDragDrop Insert. /// Sent when dropping an icon into a hotbar/inventory slot or similar. /// DragDropInsert = 53, - /// - /// AtkComponentDragDrop Can Accept Check. - /// - DragDropCanAcceptCheck = 54, - /// /// AtkComponentDragDrop Roll Over. /// @@ -205,18 +165,23 @@ public enum AddonEventType : byte DragDropDiscard = 57, /// - /// AtkComponentDragDrop Click. - /// Sent on MouseUp if the cursor has not moved since DragDropBegin, OR on MouseDown over a locked icon. + /// Drag Drop Unknown. /// - DragDropClick = 58, + [Obsolete("Use DragDropDiscard", true)] + DragDropUnk54 = 54, /// /// AtkComponentDragDrop Cancel. /// Sent on MouseUp if the cursor has not moved since DragDropBegin, OR on MouseDown over a locked icon. /// - [Obsolete("Renamed to DragDropClick")] DragDropCancel = 58, + /// + /// Drag Drop Unknown. + /// + [Obsolete("Use DragDropCancel", true)] + DragDropUnk55 = 55, + /// /// AtkComponentIconText Roll Over. /// @@ -252,11 +217,6 @@ public enum AddonEventType : byte /// TimerEnd = 65, - /// - /// AtkTimer Start. - /// - TimerStart = 66, - /// /// AtkSimpleTween Progress. /// @@ -287,11 +247,6 @@ public enum AddonEventType : byte /// WindowChangeScale = 72, - /// - /// AtkTimeline Active Label Changed. - /// - TimelineActiveLabelChanged = 75, - /// /// AtkTextNode Link Mouse Click. /// diff --git a/Dalamud/Game/Addon/Events/PluginEventController.cs b/Dalamud/Game/Addon/Events/PluginEventController.cs index 076c39cbb..afaee9966 100644 --- a/Dalamud/Game/Addon/Events/PluginEventController.cs +++ b/Dalamud/Game/Addon/Events/PluginEventController.cs @@ -5,6 +5,7 @@ using Dalamud.Game.Addon.Events.EventDataTypes; using Dalamud.Game.Gui; using Dalamud.Logging.Internal; using Dalamud.Plugin.Services; +using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Component.GUI; @@ -15,7 +16,7 @@ namespace Dalamud.Game.Addon.Events; /// internal unsafe class PluginEventController : IDisposable { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("AddonEventManager"); /// /// Initializes a new instance of the class. @@ -27,7 +28,7 @@ internal unsafe class PluginEventController : IDisposable private AddonEventListener EventListener { get; init; } - private List Events { get; } = []; + private List Events { get; } = new(); /// /// Adds a tracked event. diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs index c4a7e8f53..c008db08f 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs @@ -5,24 +5,19 @@ namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; /// /// Base class for AddonLifecycle AddonArgTypes. /// -public class AddonArgs +public abstract unsafe class AddonArgs { /// /// Constant string representing the name of an addon that is invalid. /// public const string InvalidAddon = "NullAddon"; - /// - /// Initializes a new instance of the class. - /// - internal AddonArgs() - { - } + private string? addonName; /// /// Gets the name of the addon this args referrers to. /// - public string AddonName { get; private set; } = InvalidAddon; + public string AddonName => this.GetAddonName(); /// /// Gets the pointer to the addons AtkUnitBase. @@ -30,17 +25,55 @@ public class AddonArgs public AtkUnitBasePtr Addon { get; - internal set - { - field = value; - - if (!this.Addon.IsNull && !string.IsNullOrEmpty(value.Name)) - this.AddonName = value.Name; - } + internal set; } /// /// Gets the type of these args. /// - public virtual AddonArgsType Type => AddonArgsType.Generic; + public abstract AddonArgsType Type { get; } + + /// + /// Checks if addon name matches the given span of char. + /// + /// The name to check. + /// Whether it is the case. + internal bool IsAddon(string name) + { + if (this.Addon.IsNull) + return false; + + if (name.Length is 0 or > 32) + return false; + + if (string.IsNullOrEmpty(this.Addon.Name)) + return false; + + return name == this.Addon.Name; + } + + /// + /// Clears this AddonArgs values. + /// + internal virtual void Clear() + { + this.addonName = null; + this.Addon = 0; + } + + /// + /// Helper method for ensuring the name of the addon is valid. + /// + /// The name of the addon for this object. when invalid. + private string GetAddonName() + { + if (this.Addon.IsNull) return InvalidAddon; + + var name = this.Addon.Name; + + if (string.IsNullOrEmpty(name)) + return InvalidAddon; + + return this.addonName ??= name; + } } diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonCloseArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonCloseArgs.cs deleted file mode 100644 index db3e442f8..000000000 --- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonCloseArgs.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; - -/// -/// Addon argument data for Close events. -/// -public class AddonCloseArgs : AddonArgs -{ - /// - /// Initializes a new instance of the class. - /// - internal AddonCloseArgs() - { - } - - /// - public override AddonArgsType Type => AddonArgsType.Close; - - /// - /// Gets or sets a value indicating whether the window should fire the callback method on close. - /// - public bool FireCallback { get; set; } -} diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonDrawArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonDrawArgs.cs new file mode 100644 index 000000000..989e11912 --- /dev/null +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonDrawArgs.cs @@ -0,0 +1,24 @@ +namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; + +/// +/// Addon argument data for Draw events. +/// +public class AddonDrawArgs : AddonArgs, ICloneable +{ + /// + /// Initializes a new instance of the class. + /// + [Obsolete("Not intended for public construction.", false)] + public AddonDrawArgs() + { + } + + /// + public override AddonArgsType Type => AddonArgsType.Draw; + + /// + public AddonDrawArgs Clone() => (AddonDrawArgs)this.MemberwiseClone(); + + /// + object ICloneable.Clone() => this.Clone(); +} diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFinalizeArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFinalizeArgs.cs new file mode 100644 index 000000000..d9401b414 --- /dev/null +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFinalizeArgs.cs @@ -0,0 +1,24 @@ +namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; + +/// +/// Addon argument data for ReceiveEvent events. +/// +public class AddonFinalizeArgs : AddonArgs, ICloneable +{ + /// + /// Initializes a new instance of the class. + /// + [Obsolete("Not intended for public construction.", false)] + public AddonFinalizeArgs() + { + } + + /// + public override AddonArgsType Type => AddonArgsType.Finalize; + + /// + public AddonFinalizeArgs Clone() => (AddonFinalizeArgs)this.MemberwiseClone(); + + /// + object ICloneable.Clone() => this.Clone(); +} diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFocusChangedArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFocusChangedArgs.cs deleted file mode 100644 index 8936a233b..000000000 --- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFocusChangedArgs.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; - -/// -/// Addon argument data for OnFocusChanged events. -/// -public class AddonFocusChangedArgs : AddonArgs -{ - /// - /// Initializes a new instance of the class. - /// - internal AddonFocusChangedArgs() - { - } - - /// - public override AddonArgsType Type => AddonArgsType.FocusChanged; - - /// - /// Gets or sets a value indicating whether the window is being focused or unfocused. - /// - public bool ShouldFocus { get; set; } -} diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonHideArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonHideArgs.cs deleted file mode 100644 index 3e3521bd0..000000000 --- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonHideArgs.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; - -/// -/// Addon argument data for Hide events. -/// -public class AddonHideArgs : AddonArgs -{ - /// - /// Initializes a new instance of the class. - /// - internal AddonHideArgs() - { - } - - /// - public override AddonArgsType Type => AddonArgsType.Hide; - - /// - /// Gets or sets a value indicating whether to call the hide callback handler when this hides. - /// - public bool CallHideCallback { get; set; } - - /// - /// Gets or sets the flags that the window will set when it Shows/Hides. - /// - public uint SetShowHideFlags { get; set; } - - /// - /// Gets or sets a value indicating whether something for this event message. - /// - internal bool UnknownBool { get; set; } -} diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonReceiveEventArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonReceiveEventArgs.cs index 785cd199f..980fe4f2f 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonReceiveEventArgs.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonReceiveEventArgs.cs @@ -3,12 +3,13 @@ namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; /// /// Addon argument data for ReceiveEvent events. /// -public class AddonReceiveEventArgs : AddonArgs +public class AddonReceiveEventArgs : AddonArgs, ICloneable { /// /// Initializes a new instance of the class. /// - internal AddonReceiveEventArgs() + [Obsolete("Not intended for public construction.", false)] + public AddonReceiveEventArgs() { } @@ -31,7 +32,23 @@ public class AddonReceiveEventArgs : AddonArgs public nint AtkEvent { get; set; } /// - /// Gets or sets the pointer to an AtkEventData for this event message. + /// Gets or sets the pointer to a block of data for this event message. /// - public nint AtkEventData { get; set; } + public nint Data { get; set; } + + /// + public AddonReceiveEventArgs Clone() => (AddonReceiveEventArgs)this.MemberwiseClone(); + + /// + object ICloneable.Clone() => this.Clone(); + + /// + internal override void Clear() + { + base.Clear(); + this.AtkEventType = default; + this.EventParam = default; + this.AtkEvent = default; + this.Data = default; + } } diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRefreshArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRefreshArgs.cs index d81d262bf..d28631c3c 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRefreshArgs.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRefreshArgs.cs @@ -1,22 +1,17 @@ -using System.Collections.Generic; - -using Dalamud.Game.NativeWrapper; -using Dalamud.Utility; - using FFXIVClientStructs.FFXIV.Component.GUI; -using FFXIVClientStructs.Interop; namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; /// /// Addon argument data for Refresh events. /// -public class AddonRefreshArgs : AddonArgs +public class AddonRefreshArgs : AddonArgs, ICloneable { /// /// Initializes a new instance of the class. /// - internal AddonRefreshArgs() + [Obsolete("Not intended for public construction.", false)] + public AddonRefreshArgs() { } @@ -36,32 +31,19 @@ public class AddonRefreshArgs : AddonArgs /// /// Gets the AtkValues in the form of a span. /// - [Obsolete("Pending removal, Use AtkValueEnumerable instead.")] - [Api15ToDo("Make this internal, remove obsolete")] public unsafe Span AtkValueSpan => new(this.AtkValues.ToPointer(), (int)this.AtkValueCount); - /// - /// Gets an enumerable collection of of the event's AtkValues. - /// - /// - /// An of corresponding to the event's AtkValues. - /// - public IEnumerable AtkValueEnumerable - { - get - { - for (var i = 0; i < this.AtkValueCount; i++) - { - AtkValuePtr ptr; - unsafe - { -#pragma warning disable CS0618 // Type or member is obsolete - ptr = new AtkValuePtr((nint)this.AtkValueSpan.GetPointer(i)); -#pragma warning restore CS0618 // Type or member is obsolete - } + /// + public AddonRefreshArgs Clone() => (AddonRefreshArgs)this.MemberwiseClone(); - yield return ptr; - } - } + /// + object ICloneable.Clone() => this.Clone(); + + /// + internal override void Clear() + { + base.Clear(); + this.AtkValueCount = default; + this.AtkValues = default; } } diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs index 7005b77c2..e87a980fd 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs @@ -3,12 +3,13 @@ namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; /// /// Addon argument data for OnRequestedUpdate events. /// -public class AddonRequestedUpdateArgs : AddonArgs +public class AddonRequestedUpdateArgs : AddonArgs, ICloneable { /// /// Initializes a new instance of the class. /// - internal AddonRequestedUpdateArgs() + [Obsolete("Not intended for public construction.", false)] + public AddonRequestedUpdateArgs() { } @@ -24,4 +25,18 @@ public class AddonRequestedUpdateArgs : AddonArgs /// Gets or sets the StringArrayData** for this event. /// public nint StringArrayData { get; set; } + + /// + public AddonRequestedUpdateArgs Clone() => (AddonRequestedUpdateArgs)this.MemberwiseClone(); + + /// + object ICloneable.Clone() => this.Clone(); + + /// + internal override void Clear() + { + base.Clear(); + this.NumberArrayData = default; + this.StringArrayData = default; + } } diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonSetupArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonSetupArgs.cs index 1cc0eacf3..0dd9ecee2 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonSetupArgs.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonSetupArgs.cs @@ -1,22 +1,17 @@ -using System.Collections.Generic; - -using Dalamud.Game.NativeWrapper; -using Dalamud.Utility; - using FFXIVClientStructs.FFXIV.Component.GUI; -using FFXIVClientStructs.Interop; namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; /// /// Addon argument data for Setup events. /// -public class AddonSetupArgs : AddonArgs +public class AddonSetupArgs : AddonArgs, ICloneable { /// /// Initializes a new instance of the class. /// - internal AddonSetupArgs() + [Obsolete("Not intended for public construction.", false)] + public AddonSetupArgs() { } @@ -36,32 +31,19 @@ public class AddonSetupArgs : AddonArgs /// /// Gets the AtkValues in the form of a span. /// - [Obsolete("Pending removal, Use AtkValueEnumerable instead.")] - [Api15ToDo("Make this internal, remove obsolete")] public unsafe Span AtkValueSpan => new(this.AtkValues.ToPointer(), (int)this.AtkValueCount); - /// - /// Gets an enumerable collection of of the event's AtkValues. - /// - /// - /// An of corresponding to the event's AtkValues. - /// - public IEnumerable AtkValueEnumerable - { - get - { - for (var i = 0; i < this.AtkValueCount; i++) - { - AtkValuePtr ptr; - unsafe - { -#pragma warning disable CS0618 // Type or member is obsolete - ptr = new AtkValuePtr((nint)this.AtkValueSpan.GetPointer(i)); -#pragma warning restore CS0618 // Type or member is obsolete - } + /// + public AddonSetupArgs Clone() => (AddonSetupArgs)this.MemberwiseClone(); - yield return ptr; - } - } + /// + object ICloneable.Clone() => this.Clone(); + + /// + internal override void Clear() + { + base.Clear(); + this.AtkValueCount = default; + this.AtkValues = default; } } diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonShowArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonShowArgs.cs deleted file mode 100644 index 3153d1208..000000000 --- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonShowArgs.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; - -/// -/// Addon argument data for Show events. -/// -public class AddonShowArgs : AddonArgs -{ - /// - /// Initializes a new instance of the class. - /// - internal AddonShowArgs() - { - } - - /// - public override AddonArgsType Type => AddonArgsType.Show; - - /// - /// Gets or sets a value indicating whether the window should play open sound effects. - /// - public bool SilenceOpenSoundEffect { get; set; } - - /// - /// Gets or sets the flags that the window will unset when it Shows/Hides. - /// - public uint UnsetShowHideFlags { get; set; } -} diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonUpdateArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonUpdateArgs.cs new file mode 100644 index 000000000..a263f6ae4 --- /dev/null +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonUpdateArgs.cs @@ -0,0 +1,45 @@ +namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes; + +/// +/// Addon argument data for Update events. +/// +public class AddonUpdateArgs : AddonArgs, ICloneable +{ + /// + /// Initializes a new instance of the class. + /// + [Obsolete("Not intended for public construction.", false)] + public AddonUpdateArgs() + { + } + + /// + public override AddonArgsType Type => AddonArgsType.Update; + + /// + /// Gets the time since the last update. + /// + public float TimeDelta + { + get => this.TimeDeltaInternal; + init => this.TimeDeltaInternal = value; + } + + /// + /// Gets or sets the time since the last update. + /// + internal float TimeDeltaInternal { get; set; } + + /// + public AddonUpdateArgs Clone() => (AddonUpdateArgs)this.MemberwiseClone(); + + /// + object ICloneable.Clone() => this.Clone(); + + /// + internal override void Clear() + { + base.Clear(); + this.TimeDeltaInternal = default; + } +} diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgsType.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgsType.cs index bc48eeed0..b58b5f4c7 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonArgsType.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgsType.cs @@ -5,48 +5,38 @@ /// public enum AddonArgsType { - /// - /// Generic arg type that contains no meaningful data. - /// - Generic, - /// /// Contains argument data for Setup. /// Setup, - + + /// + /// Contains argument data for Update. + /// + Update, + + /// + /// Contains argument data for Draw. + /// + Draw, + + /// + /// Contains argument data for Finalize. + /// + Finalize, + /// /// Contains argument data for RequestedUpdate. - /// + /// RequestedUpdate, - + /// /// Contains argument data for Refresh. - /// + /// Refresh, - + /// /// Contains argument data for ReceiveEvent. /// ReceiveEvent, - - /// - /// Contains argument data for Show. - /// - Show, - - /// - /// Contains argument data for Hide. - /// - Hide, - - /// - /// Contains argument data for Close. - /// - Close, - - /// - /// Contains argument data for OnFocusChanged. - /// - FocusChanged, } diff --git a/Dalamud/Game/Addon/Lifecycle/AddonEvent.cs b/Dalamud/Game/Addon/Lifecycle/AddonEvent.cs index 74c84d754..5fd0ac964 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonEvent.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonEvent.cs @@ -16,7 +16,7 @@ public enum AddonEvent /// /// PreSetup, - + /// /// An event that is fired after an addon has finished its initial setup. This event is particularly useful for /// developers seeking to add custom elements to now-initialized and populated node lists, as well as reading data @@ -29,6 +29,7 @@ public enum AddonEvent /// An event that is fired before an addon begins its update cycle via . This event /// is fired every frame that an addon is loaded, regardless of visibility. /// + /// PreUpdate, /// @@ -41,6 +42,7 @@ public enum AddonEvent /// An event that is fired before an addon begins drawing to screen via . Unlike /// , this event is only fired if an addon is visible or otherwise drawing to screen. /// + /// PreDraw, /// @@ -60,8 +62,9 @@ public enum AddonEvent ///
/// As this is part of the destruction process for an addon, this event does not have an associated Post event. /// + /// PreFinalize, - + /// /// An event that is fired before a call to is made in response to a /// change in the subscribed or @@ -78,13 +81,13 @@ public enum AddonEvent /// to the Free Company's overview. /// PreRequestedUpdate, - + /// /// An event that is fired after an addon has finished processing an ArrayData update. /// See for more information. /// PostRequestedUpdate, - + /// /// An event that is fired before an addon calls its method. Refreshes are /// generally triggered in response to certain user interactions such as changing tabs, and are primarily used to @@ -93,13 +96,13 @@ public enum AddonEvent /// /// PreRefresh, - + /// /// An event that is fired after an addon has finished its refresh. /// See for more information. /// PostRefresh, - + /// /// An event that is fired before an addon begins processing a user-driven event via /// , such as mousing over an element or clicking a button. This event @@ -109,108 +112,10 @@ public enum AddonEvent /// /// PreReceiveEvent, - + /// /// An event that is fired after an addon finishes calling its method. /// See for more information. /// PostReceiveEvent, - - /// - /// An event that is fired before an addon processes its open method. - /// - PreOpen, - - /// - /// An event that is fired after an addon has processed its open method. - /// - PostOpen, - - /// - /// An even that is fired before an addon processes its Close method. - /// - PreClose, - - /// - /// An event that is fired after an addon has processed its Close method. - /// - PostClose, - - /// - /// An event that is fired before an addon processes its Show method. - /// - PreShow, - - /// - /// An event that is fired after an addon has processed its Show method. - /// - PostShow, - - /// - /// An event that is fired before an addon processes its Hide method. - /// - PreHide, - - /// - /// An event that is fired after an addon has processed its Hide method. - /// - PostHide, - - /// - /// An event that is fired before an addon processes its OnMove method. - /// OnMove is triggered only when a move is completed. - /// - PreMove, - - /// - /// An event that is fired after an addon has processed its OnMove method. - /// OnMove is triggered only when a move is completed. - /// - PostMove, - - /// - /// An event that is fired before an addon processes its MouseOver method. - /// - PreMouseOver, - - /// - /// An event that is fired after an addon has processed its MouseOver method. - /// - PostMouseOver, - - /// - /// An event that is fired before an addon processes its MouseOut method. - /// - PreMouseOut, - - /// - /// An event that is fired after an addon has processed its MouseOut method. - /// - PostMouseOut, - - /// - /// An event that is fired before an addon processes its Focus method. - /// - /// - /// Be aware this is only called for certain popup windows, it is not triggered when clicking on windows. - /// - PreFocus, - - /// - /// An event that is fired after an addon has processed its Focus method. - /// - /// - /// Be aware this is only called for certain popup windows, it is not triggered when clicking on windows. - /// - PostFocus, - - /// - /// An event that is fired before an addon processes its FocusChanged method. - /// - PreFocusChanged, - - /// - /// An event that is fired after a addon processes its FocusChanged method. - /// - PostFocusChanged, } diff --git a/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs index 2e5439ed0..b44ab8764 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs @@ -1,15 +1,16 @@ using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; using Dalamud.Hooking; +using Dalamud.Hooking.Internal; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Logging.Internal; using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Component.GUI; namespace Dalamud.Game.Addon.Lifecycle; @@ -20,56 +21,75 @@ namespace Dalamud.Game.Addon.Lifecycle; [ServiceManager.EarlyLoadedService] internal unsafe class AddonLifecycle : IInternalDisposableService { - /// - /// Gets a list of all allocated addon virtual tables. - /// - public static readonly List AllocatedTables = []; - - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("AddonLifecycle"); [ServiceManager.ServiceDependency] private readonly Framework framework = Service.Get(); - private Hook? onInitializeAddonHook; - private bool isInvokingListeners; + [ServiceManager.ServiceDependency] + private readonly AddonLifecyclePooledArgs argsPool = Service.Get(); + + private readonly nint disallowedReceiveEventAddress; + + private readonly AddonLifecycleAddressResolver address; + private readonly AddonSetupHook onAddonSetupHook; + private readonly Hook onAddonFinalizeHook; + private readonly CallHook onAddonDrawHook; + private readonly CallHook onAddonUpdateHook; + private readonly Hook onAddonRefreshHook; + private readonly CallHook onAddonRequestedUpdateHook; [ServiceManager.ServiceConstructor] - private AddonLifecycle() + private AddonLifecycle(TargetSigScanner sigScanner) { - this.onInitializeAddonHook = Hook.FromAddress((nint)AtkUnitBase.StaticVirtualTablePointer->Initialize, this.OnAddonInitialize); - this.onInitializeAddonHook.Enable(); + this.address = new AddonLifecycleAddressResolver(); + this.address.Setup(sigScanner); + + this.disallowedReceiveEventAddress = (nint)AtkUnitBase.StaticVirtualTablePointer->ReceiveEvent; + + var refreshAddonAddress = (nint)RaptureAtkUnitManager.StaticVirtualTablePointer->RefreshAddon; + + this.onAddonSetupHook = new AddonSetupHook(this.address.AddonSetup, this.OnAddonSetup); + this.onAddonFinalizeHook = Hook.FromAddress(this.address.AddonFinalize, this.OnAddonFinalize); + this.onAddonDrawHook = new CallHook(this.address.AddonDraw, this.OnAddonDraw); + this.onAddonUpdateHook = new CallHook(this.address.AddonUpdate, this.OnAddonUpdate); + this.onAddonRefreshHook = Hook.FromAddress(refreshAddonAddress, this.OnAddonRefresh); + this.onAddonRequestedUpdateHook = new CallHook(this.address.AddonOnRequestedUpdate, this.OnRequestedUpdate); + + this.onAddonSetupHook.Enable(); + this.onAddonFinalizeHook.Enable(); + this.onAddonDrawHook.Enable(); + this.onAddonUpdateHook.Enable(); + this.onAddonRefreshHook.Enable(); + this.onAddonRequestedUpdateHook.Enable(); } + private delegate void AddonFinalizeDelegate(AtkUnitManager* unitManager, AtkUnitBase** atkUnitBase); + + /// + /// Gets a list of all AddonLifecycle ReceiveEvent Listener Hooks. + /// + internal List ReceiveEventListeners { get; } = new(); + /// /// Gets a list of all AddonLifecycle Event Listeners. - ///
- /// Mapping is: EventType -> AddonName -> ListenerList - internal Dictionary>> EventListeners { get; } = []; + ///
+ internal List EventListeners { get; } = new(); /// void IInternalDisposableService.DisposeService() { - this.onInitializeAddonHook?.Dispose(); - this.onInitializeAddonHook = null; + this.onAddonSetupHook.Dispose(); + this.onAddonFinalizeHook.Dispose(); + this.onAddonDrawHook.Dispose(); + this.onAddonUpdateHook.Dispose(); + this.onAddonRefreshHook.Dispose(); + this.onAddonRequestedUpdateHook.Dispose(); - AllocatedTables.ForEach(entry => entry.Dispose()); - AllocatedTables.Clear(); - } - - /// - /// Resolves a virtual table address to the original virtual table address. - /// - /// The modified address to resolve. - /// The original address. - internal static AtkUnitBase.AtkUnitBaseVirtualTable* GetOriginalVirtualTable(AtkUnitBase.AtkUnitBaseVirtualTable* tableAddress) - { - var matchedTable = AllocatedTables.FirstOrDefault(table => table.ModifiedVirtualTable == tableAddress); - if (matchedTable == null) + foreach (var receiveEventListener in this.ReceiveEventListeners) { - return null; + receiveEventListener.Dispose(); } - - return matchedTable.OriginalVirtualTable; } /// @@ -78,14 +98,20 @@ internal unsafe class AddonLifecycle : IInternalDisposableService /// The listener to register. internal void RegisterListener(AddonLifecycleEventListener listener) { - if (this.isInvokingListeners) + this.framework.RunOnTick(() => { - this.framework.RunOnTick(() => this.RegisterListenerMethod(listener)); - } - else - { - this.framework.RunOnFrameworkThread(() => this.RegisterListenerMethod(listener)); - } + this.EventListeners.Add(listener); + + // If we want receive event messages have an already active addon, enable the receive event hook. + // If the addon isn't active yet, we'll grab the hook when it sets up. + if (listener is { EventType: AddonEvent.PreReceiveEvent or AddonEvent.PostReceiveEvent }) + { + if (this.ReceiveEventListeners.FirstOrDefault(listeners => listeners.AddonNames.Contains(listener.AddonName)) is { } receiveEventListener) + { + receiveEventListener.TryEnable(); + } + } + }); } /// @@ -94,16 +120,27 @@ internal unsafe class AddonLifecycle : IInternalDisposableService /// The listener to unregister. internal void UnregisterListener(AddonLifecycleEventListener listener) { - listener.IsRequestedToClear = true; + // Set removed state to true immediately, then lazily remove it from the EventListeners list on next Framework Update. + listener.Removed = true; - if (this.isInvokingListeners) + this.framework.RunOnTick(() => { - this.framework.RunOnTick(() => this.UnregisterListenerMethod(listener)); - } - else - { - this.framework.RunOnFrameworkThread(() => this.UnregisterListenerMethod(listener)); - } + this.EventListeners.Remove(listener); + + // If we are disabling an ReceiveEvent listener, check if we should disable the hook. + if (listener is { EventType: AddonEvent.PreReceiveEvent or AddonEvent.PostReceiveEvent }) + { + // Get the ReceiveEvent Listener for this addon + if (this.ReceiveEventListeners.FirstOrDefault(listeners => listeners.AddonNames.Contains(listener.AddonName)) is { } receiveEventListener) + { + // If there are no other listeners listening for this event, disable the hook. + if (!this.EventListeners.Any(listeners => listeners.AddonName.Contains(listener.AddonName) && listener.EventType is AddonEvent.PreReceiveEvent or AddonEvent.PostReceiveEvent)) + { + receiveEventListener.Disable(); + } + } + } + }); } /// @@ -114,104 +151,226 @@ internal unsafe class AddonLifecycle : IInternalDisposableService /// What to blame on errors. internal void InvokeListenersSafely(AddonEvent eventType, AddonArgs args, [CallerMemberName] string blame = "") { - this.isInvokingListeners = true; - - // Early return if we don't have any listeners of this type - if (!this.EventListeners.TryGetValue(eventType, out var addonListeners)) return; - - // Handle listeners for this event type that don't care which addon is triggering it - if (addonListeners.TryGetValue(string.Empty, out var globalListeners)) + // Do not use linq; this is a high-traffic function, and more heap allocations avoided, the better. + foreach (var listener in this.EventListeners) { - foreach (var listener in globalListeners) + if (listener.EventType != eventType) + continue; + + // If the listener is pending removal, and is waiting until the next Framework Update, don't invoke listener. + if (listener.Removed) + continue; + + // Match on string.empty for listeners that want events for all addons. + if (!string.IsNullOrWhiteSpace(listener.AddonName) && !args.IsAddon(listener.AddonName)) + continue; + + try { - if (listener.IsRequestedToClear) continue; - - try - { - listener.FunctionDelegate.Invoke(eventType, args); - } - catch (Exception e) - { - Log.Error(e, $"Exception in {blame} during {eventType} invoke, for global addon event listener."); - } + listener.FunctionDelegate.Invoke(eventType, args); + } + catch (Exception e) + { + Log.Error(e, $"Exception in {blame} during {eventType} invoke."); } } - - // Handle listeners that are listening for this addon and event type specifically - if (addonListeners.TryGetValue(args.AddonName, out var addonListener)) - { - foreach (var listener in addonListener) - { - if (listener.IsRequestedToClear) continue; - - try - { - listener.FunctionDelegate.Invoke(eventType, args); - } - catch (Exception e) - { - Log.Error(e, $"Exception in {blame} during {eventType} invoke, for specific addon {args.AddonName}."); - } - } - } - - this.isInvokingListeners = false; } - private void RegisterListenerMethod(AddonLifecycleEventListener listener) + private void RegisterReceiveEventHook(AtkUnitBase* addon) { - if (!this.EventListeners.ContainsKey(listener.EventType)) + // Hook the addon's ReceiveEvent function here, but only enable the hook if we have an active listener. + // Disallows hooking the core internal event handler. + var addonName = addon->NameString; + var receiveEventAddress = (nint)addon->VirtualTable->ReceiveEvent; + if (receiveEventAddress != this.disallowedReceiveEventAddress) { - if (!this.EventListeners.TryAdd(listener.EventType, [])) + // If we have a ReceiveEvent listener already made for this hook address, add this addon's name to that handler. + if (this.ReceiveEventListeners.FirstOrDefault(listener => listener.FunctionAddress == receiveEventAddress) is { } existingListener) { - return; + if (!existingListener.AddonNames.Contains(addonName)) + { + existingListener.AddonNames.Add(addonName); + } + } + + // Else, we have an addon that we don't have the ReceiveEvent for yet, make it. + else + { + this.ReceiveEventListeners.Add(new AddonLifecycleReceiveEventListener(this, addonName, receiveEventAddress)); + } + + // If we have an active listener for this addon already, we need to activate this hook. + if (this.EventListeners.Any(listener => (listener.EventType is AddonEvent.PostReceiveEvent or AddonEvent.PreReceiveEvent) && listener.AddonName == addonName)) + { + if (this.ReceiveEventListeners.FirstOrDefault(listener => listener.AddonNames.Contains(addonName)) is { } receiveEventListener) + { + receiveEventListener.TryEnable(); + } } } - - // Note: string.Empty is a valid addon name, as that will trigger on any addon for this event type - if (!this.EventListeners[listener.EventType].ContainsKey(listener.AddonName)) - { - if (!this.EventListeners[listener.EventType].TryAdd(listener.AddonName, [])) - { - return; - } - } - - this.EventListeners[listener.EventType][listener.AddonName].Add(listener); } - private void UnregisterListenerMethod(AddonLifecycleEventListener listener) + private void UnregisterReceiveEventHook(string addonName) { - if (this.EventListeners.TryGetValue(listener.EventType, out var addonListeners)) + // Remove this addons ReceiveEvent Registration + if (this.ReceiveEventListeners.FirstOrDefault(listener => listener.AddonNames.Contains(addonName)) is { } eventListener) { - if (addonListeners.TryGetValue(listener.AddonName, out var addonListener)) + eventListener.AddonNames.Remove(addonName); + + // If there are no more listeners let's remove and dispose. + if (eventListener.AddonNames.Count is 0) { - addonListener.Remove(listener); + this.ReceiveEventListeners.Remove(eventListener); + eventListener.Dispose(); } } } - private void OnAddonInitialize(AtkUnitBase* addon) + private void OnAddonSetup(AtkUnitBase* addon, uint valueCount, AtkValue* values) { try { - this.LogInitialize(addon->NameString); - - // AddonVirtualTable class handles creating the virtual table, and overriding each of the tracked virtual functions - AllocatedTables.Add(new AddonVirtualTable(addon, this)); + this.RegisterReceiveEventHook(addon); } catch (Exception e) { - Log.Error(e, "Exception in AddonLifecycle during OnAddonInitialize."); + Log.Error(e, "Exception in OnAddonSetup ReceiveEvent Registration."); } - this.onInitializeAddonHook!.Original(addon); + using var returner = this.argsPool.Rent(out AddonSetupArgs arg); + arg.Clear(); + arg.Addon = (nint)addon; + arg.AtkValueCount = valueCount; + arg.AtkValues = (nint)values; + this.InvokeListenersSafely(AddonEvent.PreSetup, arg); + valueCount = arg.AtkValueCount; + values = (AtkValue*)arg.AtkValues; + + try + { + addon->OnSetup(valueCount, values); + } + catch (Exception e) + { + Log.Error(e, "Caught exception when calling original AddonSetup. This may be a bug in the game or another plugin hooking this method."); + } + + this.InvokeListenersSafely(AddonEvent.PostSetup, arg); } - [Conditional("DEBUG")] - private void LogInitialize(string addonName) + private void OnAddonFinalize(AtkUnitManager* unitManager, AtkUnitBase** atkUnitBase) { - Log.Debug($"Initializing {addonName}"); + try + { + var addonName = atkUnitBase[0]->NameString; + this.UnregisterReceiveEventHook(addonName); + } + catch (Exception e) + { + Log.Error(e, "Exception in OnAddonFinalize ReceiveEvent Removal."); + } + + using var returner = this.argsPool.Rent(out AddonFinalizeArgs arg); + arg.Clear(); + arg.Addon = (nint)atkUnitBase[0]; + this.InvokeListenersSafely(AddonEvent.PreFinalize, arg); + + try + { + this.onAddonFinalizeHook.Original(unitManager, atkUnitBase); + } + catch (Exception e) + { + Log.Error(e, "Caught exception when calling original AddonFinalize. This may be a bug in the game or another plugin hooking this method."); + } + } + + private void OnAddonDraw(AtkUnitBase* addon) + { + using var returner = this.argsPool.Rent(out AddonDrawArgs arg); + arg.Clear(); + arg.Addon = (nint)addon; + this.InvokeListenersSafely(AddonEvent.PreDraw, arg); + + try + { + addon->Draw(); + } + catch (Exception e) + { + Log.Error(e, "Caught exception when calling original AddonDraw. This may be a bug in the game or another plugin hooking this method."); + } + + this.InvokeListenersSafely(AddonEvent.PostDraw, arg); + } + + private void OnAddonUpdate(AtkUnitBase* addon, float delta) + { + using var returner = this.argsPool.Rent(out AddonUpdateArgs arg); + arg.Clear(); + arg.Addon = (nint)addon; + arg.TimeDeltaInternal = delta; + this.InvokeListenersSafely(AddonEvent.PreUpdate, arg); + + try + { + addon->Update(delta); + } + catch (Exception e) + { + Log.Error(e, "Caught exception when calling original AddonUpdate. This may be a bug in the game or another plugin hooking this method."); + } + + this.InvokeListenersSafely(AddonEvent.PostUpdate, arg); + } + + private bool OnAddonRefresh(AtkUnitManager* thisPtr, AtkUnitBase* addon, uint valueCount, AtkValue* values) + { + var result = false; + + using var returner = this.argsPool.Rent(out AddonRefreshArgs arg); + arg.Clear(); + arg.Addon = (nint)addon; + arg.AtkValueCount = valueCount; + arg.AtkValues = (nint)values; + this.InvokeListenersSafely(AddonEvent.PreRefresh, arg); + valueCount = arg.AtkValueCount; + values = (AtkValue*)arg.AtkValues; + + try + { + result = this.onAddonRefreshHook.Original(thisPtr, addon, valueCount, values); + } + catch (Exception e) + { + Log.Error(e, "Caught exception when calling original AddonRefresh. This may be a bug in the game or another plugin hooking this method."); + } + + this.InvokeListenersSafely(AddonEvent.PostRefresh, arg); + return result; + } + + private void OnRequestedUpdate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData) + { + using var returner = this.argsPool.Rent(out AddonRequestedUpdateArgs arg); + arg.Clear(); + arg.Addon = (nint)addon; + arg.NumberArrayData = (nint)numberArrayData; + arg.StringArrayData = (nint)stringArrayData; + this.InvokeListenersSafely(AddonEvent.PreRequestedUpdate, arg); + numberArrayData = (NumberArrayData**)arg.NumberArrayData; + stringArrayData = (StringArrayData**)arg.StringArrayData; + + try + { + addon->OnRequestedUpdate(numberArrayData, stringArrayData); + } + catch (Exception e) + { + Log.Error(e, "Caught exception when calling original AddonRequestedUpdate. This may be a bug in the game or another plugin hooking this method."); + } + + this.InvokeListenersSafely(AddonEvent.PostRequestedUpdate, arg); } } @@ -228,7 +387,7 @@ internal class AddonLifecyclePluginScoped : IInternalDisposableService, IAddonLi [ServiceManager.ServiceDependency] private readonly AddonLifecycle addonLifecycleService = Service.Get(); - private readonly List eventListeners = []; + private readonly List eventListeners = new(); /// void IInternalDisposableService.DisposeService() @@ -299,14 +458,10 @@ internal class AddonLifecyclePluginScoped : IInternalDisposableService, IAddonLi this.eventListeners.RemoveAll(entry => { if (entry.FunctionDelegate != handler) return false; - + this.addonLifecycleService.UnregisterListener(entry); return true; }); } } - - /// - public unsafe nint GetOriginalVirtualTable(nint virtualTableAddress) - => (nint)AddonLifecycle.GetOriginalVirtualTable((AtkUnitBase.AtkUnitBaseVirtualTable*)virtualTableAddress); } diff --git a/Dalamud/Game/Addon/Lifecycle/AddonLifecycleAddressResolver.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycleAddressResolver.cs new file mode 100644 index 000000000..854d666fd --- /dev/null +++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycleAddressResolver.cs @@ -0,0 +1,56 @@ +using FFXIVClientStructs.FFXIV.Component.GUI; + +namespace Dalamud.Game.Addon.Lifecycle; + +/// +/// AddonLifecycleService memory address resolver. +/// +internal unsafe class AddonLifecycleAddressResolver : BaseAddressResolver +{ + /// + /// Gets the address of the addon setup hook invoked by the AtkUnitManager. + /// There are two callsites for this vFunc, we need to hook both of them to catch both normal UI and special UI cases like dialogue. + /// This is called for a majority of all addon OnSetup's. + /// + public nint AddonSetup { get; private set; } + + /// + /// Gets the address of the other addon setup hook invoked by the AtkUnitManager. + /// There are two callsites for this vFunc, we need to hook both of them to catch both normal UI and special UI cases like dialogue. + /// This seems to be called rarely for specific addons. + /// + public nint AddonSetup2 { get; private set; } + + /// + /// Gets the address of the addon finalize hook invoked by the AtkUnitManager. + /// + public nint AddonFinalize { get; private set; } + + /// + /// Gets the address of the addon draw hook invoked by virtual function call. + /// + public nint AddonDraw { get; private set; } + + /// + /// Gets the address of the addon update hook invoked by virtual function call. + /// + public nint AddonUpdate { get; private set; } + + /// + /// Gets the address of the addon onRequestedUpdate hook invoked by virtual function call. + /// + public nint AddonOnRequestedUpdate { get; private set; } + + /// + /// Scan for and setup any configured address pointers. + /// + /// The signature scanner to facilitate setup. + protected override void Setup64Bit(ISigScanner sig) + { + this.AddonSetup = sig.ScanText("4C 8B 88 ?? ?? ?? ?? 66 44 39 BB"); + this.AddonFinalize = sig.ScanText("E8 ?? ?? ?? ?? 48 83 EF 01 75 D5"); + this.AddonDraw = sig.ScanText("FF 90 ?? ?? ?? ?? 83 EB 01 79 C4 48 81 EF ?? ?? ?? ?? 48 83 ED 01"); + this.AddonUpdate = sig.ScanText("FF 90 ?? ?? ?? ?? 40 88 AF ?? ?? ?? ?? 45 33 D2"); + this.AddonOnRequestedUpdate = sig.ScanText("FF 90 A0 01 00 00 48 8B 5C 24 30"); + } +} diff --git a/Dalamud/Game/Addon/Lifecycle/AddonLifecycleEventListener.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycleEventListener.cs index 38c081e65..9d411cdbc 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonLifecycleEventListener.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycleEventListener.cs @@ -25,19 +25,19 @@ internal class AddonLifecycleEventListener /// string.Empty if it wants to be called for any addon. /// public string AddonName { get; init; } - + + /// + /// Gets or sets a value indicating whether this event has been unregistered. + /// + public bool Removed { get; set; } + /// /// Gets the event type this listener is looking for. /// public AddonEvent EventType { get; init; } - + /// /// Gets the delegate this listener invokes. /// public IAddonLifecycle.AddonEventDelegate FunctionDelegate { get; init; } - - /// - /// Gets or sets if the listener is requested to be cleared. - /// - internal bool IsRequestedToClear { get; set; } } diff --git a/Dalamud/Game/Addon/Lifecycle/AddonLifecycleReceiveEventListener.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycleReceiveEventListener.cs new file mode 100644 index 000000000..0d2bcc7f2 --- /dev/null +++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycleReceiveEventListener.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; + +using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; +using Dalamud.Hooking; +using Dalamud.Logging.Internal; + +using FFXIVClientStructs.FFXIV.Component.GUI; + +namespace Dalamud.Game.Addon.Lifecycle; + +/// +/// This class is a helper for tracking and invoking listener delegates for Addon_OnReceiveEvent. +/// Multiple addons may use the same ReceiveEvent function, this helper makes sure that those addon events are handled properly. +/// +internal unsafe class AddonLifecycleReceiveEventListener : IDisposable +{ + private static readonly ModuleLog Log = new("AddonLifecycle"); + + [ServiceManager.ServiceDependency] + private readonly AddonLifecyclePooledArgs argsPool = Service.Get(); + + /// + /// Initializes a new instance of the class. + /// + /// AddonLifecycle service instance. + /// Initial Addon Requesting this listener. + /// Address of Addon's ReceiveEvent function. + internal AddonLifecycleReceiveEventListener(AddonLifecycle service, string addonName, nint receiveEventAddress) + { + this.AddonLifecycle = service; + this.AddonNames = [addonName]; + this.FunctionAddress = receiveEventAddress; + } + + /// + /// Gets the list of addons that use this receive event hook. + /// + public List AddonNames { get; init; } + + /// + /// Gets the address of the ReceiveEvent function as provided by the vtable on setup. + /// + public nint FunctionAddress { get; init; } + + /// + /// Gets the contained hook for these addons. + /// + public Hook? Hook { get; private set; } + + /// + /// Gets or sets the Reference to AddonLifecycle service instance. + /// + private AddonLifecycle AddonLifecycle { get; set; } + + /// + /// Try to hook and enable this receive event handler. + /// + public void TryEnable() + { + this.Hook ??= Hook.FromAddress(this.FunctionAddress, this.OnReceiveEvent); + this.Hook?.Enable(); + } + + /// + /// Disable the hook for this receive event handler. + /// + public void Disable() + { + this.Hook?.Disable(); + } + + /// + public void Dispose() + { + this.Hook?.Dispose(); + } + + private void OnReceiveEvent(AtkUnitBase* addon, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) + { + // Check that we didn't get here through a call to another addons handler. + var addonName = addon->NameString; + if (!this.AddonNames.Contains(addonName)) + { + this.Hook!.Original(addon, eventType, eventParam, atkEvent, atkEventData); + return; + } + + using var returner = this.argsPool.Rent(out AddonReceiveEventArgs arg); + arg.Clear(); + arg.Addon = (nint)addon; + arg.AtkEventType = (byte)eventType; + arg.EventParam = eventParam; + arg.AtkEvent = (IntPtr)atkEvent; + arg.Data = (nint)atkEventData; + this.AddonLifecycle.InvokeListenersSafely(AddonEvent.PreReceiveEvent, arg); + eventType = (AtkEventType)arg.AtkEventType; + eventParam = arg.EventParam; + atkEvent = (AtkEvent*)arg.AtkEvent; + atkEventData = (AtkEventData*)arg.Data; + + try + { + this.Hook!.Original(addon, eventType, eventParam, atkEvent, atkEventData); + } + catch (Exception e) + { + Log.Error(e, "Caught exception when calling original AddonReceiveEvent. This may be a bug in the game or another plugin hooking this method."); + } + + this.AddonLifecycle.InvokeListenersSafely(AddonEvent.PostReceiveEvent, arg); + } +} diff --git a/Dalamud/Game/Addon/Lifecycle/AddonSetupHook.cs b/Dalamud/Game/Addon/Lifecycle/AddonSetupHook.cs new file mode 100644 index 000000000..297323b8f --- /dev/null +++ b/Dalamud/Game/Addon/Lifecycle/AddonSetupHook.cs @@ -0,0 +1,80 @@ +using System.Runtime.InteropServices; + +using Reloaded.Hooks.Definitions; + +namespace Dalamud.Game.Addon.Lifecycle; + +/// +/// This class represents a callsite hook used to replace the address of the OnSetup function in r9. +/// +/// Delegate signature for this hook. +internal class AddonSetupHook : IDisposable where T : Delegate +{ + private readonly Reloaded.Hooks.AsmHook asmHook; + + private T? detour; + private bool activated; + + /// + /// Initializes a new instance of the class. + /// + /// Address of the instruction to replace. + /// Delegate to invoke. + internal AddonSetupHook(nint address, T detour) + { + this.detour = detour; + + var detourPtr = Marshal.GetFunctionPointerForDelegate(this.detour); + var code = new[] + { + "use64", + $"mov r9, 0x{detourPtr:X8}", + }; + + var opt = new AsmHookOptions + { + PreferRelativeJump = true, + Behaviour = Reloaded.Hooks.Definitions.Enums.AsmHookBehaviour.DoNotExecuteOriginal, + MaxOpcodeSize = 5, + }; + + this.asmHook = new Reloaded.Hooks.AsmHook(code, (nuint)address, opt); + } + + /// + /// Gets a value indicating whether the hook is enabled. + /// + public bool IsEnabled => this.asmHook.IsEnabled; + + /// + /// Starts intercepting a call to the function. + /// + public void Enable() + { + if (!this.activated) + { + this.activated = true; + this.asmHook.Activate(); + return; + } + + this.asmHook.Enable(); + } + + /// + /// Stops intercepting a call to the function. + /// + public void Disable() + { + this.asmHook.Disable(); + } + + /// + /// Remove a hook from the current process. + /// + public void Dispose() + { + this.asmHook.Disable(); + this.detour = null; + } +} diff --git a/Dalamud/Game/Addon/Lifecycle/AddonVirtualTable.cs b/Dalamud/Game/Addon/Lifecycle/AddonVirtualTable.cs deleted file mode 100644 index 1b2c828f8..000000000 --- a/Dalamud/Game/Addon/Lifecycle/AddonVirtualTable.cs +++ /dev/null @@ -1,679 +0,0 @@ -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; - -using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; -using Dalamud.Logging.Internal; - -using FFXIVClientStructs.FFXIV.Client.System.Memory; -using FFXIVClientStructs.FFXIV.Component.GUI; - -namespace Dalamud.Game.Addon.Lifecycle; - -/// -/// Represents a class that holds references to an addons original and modified virtual table entries. -/// -internal unsafe class AddonVirtualTable : IDisposable -{ - // This need to be at minimum the largest virtual table size of all addons - // Copying extra entries is not problematic, and is considered safe. - private const int VirtualTableEntryCount = 200; - - private const bool EnableLogging = false; - - private static readonly ModuleLog Log = new("LifecycleVT"); - - private readonly AddonLifecycle lifecycleService; - - // Each addon gets its own set of args that are used to mutate the original call when used in pre-calls - private readonly AddonSetupArgs setupArgs = new(); - private readonly AddonArgs finalizeArgs = new(); - private readonly AddonArgs drawArgs = new(); - private readonly AddonArgs updateArgs = new(); - private readonly AddonRefreshArgs refreshArgs = new(); - private readonly AddonRequestedUpdateArgs requestedUpdateArgs = new(); - private readonly AddonReceiveEventArgs receiveEventArgs = new(); - private readonly AddonArgs openArgs = new(); - private readonly AddonCloseArgs closeArgs = new(); - private readonly AddonShowArgs showArgs = new(); - private readonly AddonHideArgs hideArgs = new(); - private readonly AddonArgs onMoveArgs = new(); - private readonly AddonArgs onMouseOverArgs = new(); - private readonly AddonArgs onMouseOutArgs = new(); - private readonly AddonArgs focusArgs = new(); - private readonly AddonFocusChangedArgs focusChangedArgs = new(); - - private readonly AtkUnitBase* atkUnitBase; - - // Pinned Function Delegates, as these functions get assigned to an unmanaged virtual table, - // the CLR needs to know they are in use, or it will invalidate them causing random crashing. - private readonly AtkUnitBase.Delegates.Dtor destructorFunction; - private readonly AtkUnitBase.Delegates.OnSetup onSetupFunction; - private readonly AtkUnitBase.Delegates.Finalizer finalizerFunction; - private readonly AtkUnitBase.Delegates.Draw drawFunction; - private readonly AtkUnitBase.Delegates.Update updateFunction; - private readonly AtkUnitBase.Delegates.OnRefresh onRefreshFunction; - private readonly AtkUnitBase.Delegates.OnRequestedUpdate onRequestedUpdateFunction; - private readonly AtkUnitBase.Delegates.ReceiveEvent onReceiveEventFunction; - private readonly AtkUnitBase.Delegates.Open openFunction; - private readonly AtkUnitBase.Delegates.Close closeFunction; - private readonly AtkUnitBase.Delegates.Show showFunction; - private readonly AtkUnitBase.Delegates.Hide hideFunction; - private readonly AtkUnitBase.Delegates.OnMove onMoveFunction; - private readonly AtkUnitBase.Delegates.OnMouseOver onMouseOverFunction; - private readonly AtkUnitBase.Delegates.OnMouseOut onMouseOutFunction; - private readonly AtkUnitBase.Delegates.Focus focusFunction; - private readonly AtkUnitBase.Delegates.OnFocusChange onFocusChangeFunction; - - /// - /// Initializes a new instance of the class. - /// - /// AtkUnitBase* for the addon to replace the table of. - /// Reference to AddonLifecycle service to callback and invoke listeners. - internal AddonVirtualTable(AtkUnitBase* addon, AddonLifecycle lifecycleService) - { - this.atkUnitBase = addon; - this.lifecycleService = lifecycleService; - - // Save original virtual table - this.OriginalVirtualTable = addon->VirtualTable; - - // Create copy of original table - // Note this will copy any derived/overriden functions that this specific addon has. - // Note: currently there are 73 virtual functions, but there's no harm in copying more for when they add new virtual functions to the game - this.ModifiedVirtualTable = (AtkUnitBase.AtkUnitBaseVirtualTable*)IMemorySpace.GetUISpace()->Malloc(0x8 * VirtualTableEntryCount, 8); - NativeMemory.Copy(addon->VirtualTable, this.ModifiedVirtualTable, 0x8 * VirtualTableEntryCount); - - // Overwrite the addons existing virtual table with our own - addon->VirtualTable = this.ModifiedVirtualTable; - - // Pin each of our listener functions - this.destructorFunction = this.OnAddonDestructor; - this.onSetupFunction = this.OnAddonSetup; - this.finalizerFunction = this.OnAddonFinalize; - this.drawFunction = this.OnAddonDraw; - this.updateFunction = this.OnAddonUpdate; - this.onRefreshFunction = this.OnAddonRefresh; - this.onRequestedUpdateFunction = this.OnRequestedUpdate; - this.onReceiveEventFunction = this.OnAddonReceiveEvent; - this.openFunction = this.OnAddonOpen; - this.closeFunction = this.OnAddonClose; - this.showFunction = this.OnAddonShow; - this.hideFunction = this.OnAddonHide; - this.onMoveFunction = this.OnAddonMove; - this.onMouseOverFunction = this.OnAddonMouseOver; - this.onMouseOutFunction = this.OnAddonMouseOut; - this.focusFunction = this.OnAddonFocus; - this.onFocusChangeFunction = this.OnAddonFocusChange; - - // Overwrite specific virtual table entries - this.ModifiedVirtualTable->Dtor = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.destructorFunction); - this.ModifiedVirtualTable->OnSetup = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.onSetupFunction); - this.ModifiedVirtualTable->Finalizer = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.finalizerFunction); - this.ModifiedVirtualTable->Draw = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.drawFunction); - this.ModifiedVirtualTable->Update = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.updateFunction); - this.ModifiedVirtualTable->OnRefresh = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.onRefreshFunction); - this.ModifiedVirtualTable->OnRequestedUpdate = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.onRequestedUpdateFunction); - this.ModifiedVirtualTable->ReceiveEvent = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.onReceiveEventFunction); - this.ModifiedVirtualTable->Open = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.openFunction); - this.ModifiedVirtualTable->Close = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.closeFunction); - this.ModifiedVirtualTable->Show = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.showFunction); - this.ModifiedVirtualTable->Hide = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.hideFunction); - this.ModifiedVirtualTable->OnMove = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.onMoveFunction); - this.ModifiedVirtualTable->OnMouseOver = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.onMouseOverFunction); - this.ModifiedVirtualTable->OnMouseOut = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.onMouseOutFunction); - this.ModifiedVirtualTable->Focus = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.focusFunction); - this.ModifiedVirtualTable->OnFocusChange = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.onFocusChangeFunction); - } - - /// - /// Gets the original virtual table address for this addon. - /// - internal AtkUnitBase.AtkUnitBaseVirtualTable* OriginalVirtualTable { get; private set; } - - /// - /// Gets the modified virtual address for this addon. - /// - internal AtkUnitBase.AtkUnitBaseVirtualTable* ModifiedVirtualTable { get; private set; } - - /// - public void Dispose() - { - // Ensure restoration is done atomically. - Interlocked.Exchange(ref *(nint*)&this.atkUnitBase->VirtualTable, (nint)this.OriginalVirtualTable); - IMemorySpace.Free(this.ModifiedVirtualTable, 0x8 * VirtualTableEntryCount); - } - - private AtkEventListener* OnAddonDestructor(AtkUnitBase* thisPtr, byte freeFlags) - { - AtkEventListener* result = null; - - try - { - this.LogEvent(EnableLogging); - - try - { - result = this.OriginalVirtualTable->Dtor(thisPtr, freeFlags); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon Dtor. This may be a bug in the game or another plugin hooking this method."); - } - - if ((freeFlags & 1) == 1) - { - IMemorySpace.Free(this.ModifiedVirtualTable, 0x8 * VirtualTableEntryCount); - AddonLifecycle.AllocatedTables.Remove(this); - } - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonDestructor."); - } - - return result; - } - - private void OnAddonSetup(AtkUnitBase* addon, uint valueCount, AtkValue* values) - { - try - { - this.LogEvent(EnableLogging); - - this.setupArgs.Addon = addon; - this.setupArgs.AtkValueCount = valueCount; - this.setupArgs.AtkValues = (nint)values; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreSetup, this.setupArgs); - - valueCount = this.setupArgs.AtkValueCount; - values = (AtkValue*)this.setupArgs.AtkValues; - - try - { - this.OriginalVirtualTable->OnSetup(addon, valueCount, values); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon OnSetup. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostSetup, this.setupArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonSetup."); - } - } - - private void OnAddonFinalize(AtkUnitBase* thisPtr) - { - try - { - this.LogEvent(EnableLogging); - - this.finalizeArgs.Addon = thisPtr; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreFinalize, this.finalizeArgs); - - try - { - this.OriginalVirtualTable->Finalizer(thisPtr); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon Finalizer. This may be a bug in the game or another plugin hooking this method."); - } - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonFinalize."); - } - } - - private void OnAddonDraw(AtkUnitBase* addon) - { - try - { - this.LogEvent(EnableLogging); - - this.drawArgs.Addon = addon; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreDraw, this.drawArgs); - - try - { - this.OriginalVirtualTable->Draw(addon); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon Draw. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostDraw, this.drawArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonDraw."); - } - } - - private void OnAddonUpdate(AtkUnitBase* addon, float delta) - { - try - { - this.LogEvent(EnableLogging); - - this.updateArgs.Addon = addon; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreUpdate, this.updateArgs); - - // Note: Do not pass or allow manipulation of delta. - // It's realistically not something that should be needed. - // And even if someone does, they are encouraged to hook Update themselves. - - try - { - this.OriginalVirtualTable->Update(addon, delta); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon Update. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostUpdate, this.updateArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonUpdate."); - } - } - - private bool OnAddonRefresh(AtkUnitBase* addon, uint valueCount, AtkValue* values) - { - var result = false; - - try - { - this.LogEvent(EnableLogging); - - this.refreshArgs.Addon = addon; - this.refreshArgs.AtkValueCount = valueCount; - this.refreshArgs.AtkValues = (nint)values; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreRefresh, this.refreshArgs); - - valueCount = this.refreshArgs.AtkValueCount; - values = (AtkValue*)this.refreshArgs.AtkValues; - - try - { - result = this.OriginalVirtualTable->OnRefresh(addon, valueCount, values); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon OnRefresh. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostRefresh, this.refreshArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonRefresh."); - } - - return result; - } - - private void OnRequestedUpdate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData) - { - try - { - this.LogEvent(EnableLogging); - - this.requestedUpdateArgs.Addon = addon; - this.requestedUpdateArgs.NumberArrayData = (nint)numberArrayData; - this.requestedUpdateArgs.StringArrayData = (nint)stringArrayData; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreRequestedUpdate, this.requestedUpdateArgs); - - numberArrayData = (NumberArrayData**)this.requestedUpdateArgs.NumberArrayData; - stringArrayData = (StringArrayData**)this.requestedUpdateArgs.StringArrayData; - - try - { - this.OriginalVirtualTable->OnRequestedUpdate(addon, numberArrayData, stringArrayData); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon OnRequestedUpdate. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostRequestedUpdate, this.requestedUpdateArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnRequestedUpdate."); - } - } - - private void OnAddonReceiveEvent(AtkUnitBase* addon, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) - { - try - { - this.LogEvent(EnableLogging); - - this.receiveEventArgs.Addon = (nint)addon; - this.receiveEventArgs.AtkEventType = (byte)eventType; - this.receiveEventArgs.EventParam = eventParam; - this.receiveEventArgs.AtkEvent = (IntPtr)atkEvent; - this.receiveEventArgs.AtkEventData = (nint)atkEventData; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreReceiveEvent, this.receiveEventArgs); - - eventType = (AtkEventType)this.receiveEventArgs.AtkEventType; - eventParam = this.receiveEventArgs.EventParam; - atkEvent = (AtkEvent*)this.receiveEventArgs.AtkEvent; - atkEventData = (AtkEventData*)this.receiveEventArgs.AtkEventData; - - try - { - this.OriginalVirtualTable->ReceiveEvent(addon, eventType, eventParam, atkEvent, atkEventData); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon ReceiveEvent. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostReceiveEvent, this.receiveEventArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonReceiveEvent."); - } - } - - private bool OnAddonOpen(AtkUnitBase* thisPtr, uint depthLayer) - { - var result = false; - - try - { - this.LogEvent(EnableLogging); - - this.openArgs.Addon = thisPtr; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreOpen, this.openArgs); - - try - { - result = this.OriginalVirtualTable->Open(thisPtr, depthLayer); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon Open. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostOpen, this.openArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonOpen."); - } - - return result; - } - - private bool OnAddonClose(AtkUnitBase* thisPtr, bool fireCallback) - { - var result = false; - - try - { - this.LogEvent(EnableLogging); - - this.closeArgs.Addon = thisPtr; - this.closeArgs.FireCallback = fireCallback; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreClose, this.closeArgs); - - fireCallback = this.closeArgs.FireCallback; - - try - { - result = this.OriginalVirtualTable->Close(thisPtr, fireCallback); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon Close. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostClose, this.closeArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonClose."); - } - - return result; - } - - private void OnAddonShow(AtkUnitBase* thisPtr, bool silenceOpenSoundEffect, uint unsetShowHideFlags) - { - try - { - this.LogEvent(EnableLogging); - - this.showArgs.Addon = thisPtr; - this.showArgs.SilenceOpenSoundEffect = silenceOpenSoundEffect; - this.showArgs.UnsetShowHideFlags = unsetShowHideFlags; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreShow, this.showArgs); - - silenceOpenSoundEffect = this.showArgs.SilenceOpenSoundEffect; - unsetShowHideFlags = this.showArgs.UnsetShowHideFlags; - - try - { - this.OriginalVirtualTable->Show(thisPtr, silenceOpenSoundEffect, unsetShowHideFlags); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon Show. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostShow, this.showArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonShow."); - } - } - - private void OnAddonHide(AtkUnitBase* thisPtr, bool unkBool, bool callHideCallback, uint setShowHideFlags) - { - try - { - this.LogEvent(EnableLogging); - - this.hideArgs.Addon = thisPtr; - this.hideArgs.UnknownBool = unkBool; - this.hideArgs.CallHideCallback = callHideCallback; - this.hideArgs.SetShowHideFlags = setShowHideFlags; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreHide, this.hideArgs); - - unkBool = this.hideArgs.UnknownBool; - callHideCallback = this.hideArgs.CallHideCallback; - setShowHideFlags = this.hideArgs.SetShowHideFlags; - - try - { - this.OriginalVirtualTable->Hide(thisPtr, unkBool, callHideCallback, setShowHideFlags); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon Hide. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostHide, this.hideArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonHide."); - } - } - - private void OnAddonMove(AtkUnitBase* thisPtr) - { - try - { - this.LogEvent(EnableLogging); - - this.onMoveArgs.Addon = thisPtr; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreMove, this.onMoveArgs); - - try - { - this.OriginalVirtualTable->OnMove(thisPtr); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon OnMove. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostMove, this.onMoveArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonMove."); - } - } - - private void OnAddonMouseOver(AtkUnitBase* thisPtr) - { - try - { - this.LogEvent(EnableLogging); - - this.onMouseOverArgs.Addon = thisPtr; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreMouseOver, this.onMouseOverArgs); - - try - { - this.OriginalVirtualTable->OnMouseOver(thisPtr); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon OnMouseOver. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostMouseOver, this.onMouseOverArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonMouseOver."); - } - } - - private void OnAddonMouseOut(AtkUnitBase* thisPtr) - { - try - { - this.LogEvent(EnableLogging); - - this.onMouseOutArgs.Addon = thisPtr; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreMouseOut, this.onMouseOutArgs); - - try - { - this.OriginalVirtualTable->OnMouseOut(thisPtr); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon OnMouseOut. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostMouseOut, this.onMouseOutArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonMouseOut."); - } - } - - private void OnAddonFocus(AtkUnitBase* thisPtr) - { - try - { - this.LogEvent(EnableLogging); - - this.focusArgs.Addon = thisPtr; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreFocus, this.focusArgs); - - try - { - this.OriginalVirtualTable->Focus(thisPtr); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon Focus. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostFocus, this.focusArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonFocus."); - } - } - - private void OnAddonFocusChange(AtkUnitBase* thisPtr, bool isFocused) - { - try - { - this.LogEvent(EnableLogging); - - this.focusChangedArgs.Addon = thisPtr; - this.focusChangedArgs.ShouldFocus = isFocused; - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PreFocusChanged, this.focusChangedArgs); - - isFocused = this.focusChangedArgs.ShouldFocus; - - try - { - this.OriginalVirtualTable->OnFocusChange(thisPtr, isFocused); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon OnFocusChanged. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AddonEvent.PostFocusChanged, this.focusChangedArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAddonFocusChange."); - } - } - - [Conditional("DEBUG")] - private void LogEvent(bool loggingEnabled, [CallerMemberName] string caller = "") - { - if (loggingEnabled) - { - // Manually disable the really spammy log events, you can comment this out if you need to debug them. - if (caller is "OnAddonUpdate" or "OnAddonDraw" or "OnAddonReceiveEvent" or "OnRequestedUpdate") - return; - - Log.Debug($"[{caller}]: {this.atkUnitBase->NameString}"); - } - } -} diff --git a/Dalamud/Game/Agent/AgentArgTypes/AgentArgs.cs b/Dalamud/Game/Agent/AgentArgTypes/AgentArgs.cs deleted file mode 100644 index 1de80694f..000000000 --- a/Dalamud/Game/Agent/AgentArgTypes/AgentArgs.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Dalamud.Game.NativeWrapper; - -namespace Dalamud.Game.Agent.AgentArgTypes; - -/// -/// Base class for AgentLifecycle AgentArgTypes. -/// -public unsafe class AgentArgs -{ - /// - /// Initializes a new instance of the class. - /// - internal AgentArgs() - { - } - - /// - /// Gets the pointer to the Agents AgentInterface*. - /// - public AgentInterfacePtr Agent { get; internal set; } - - /// - /// Gets the agent id. - /// - public AgentId AgentId { get; internal set; } - - /// - /// Gets the type of these args. - /// - public virtual AgentArgsType Type => AgentArgsType.Generic; - - /// - /// Gets the typed pointer to the Agents AgentInterface*. - /// - /// AgentInterface. - /// Typed pointer to contained Agents AgentInterface. - public T* GetAgentPointer() where T : unmanaged - => (T*)this.Agent.Address; -} diff --git a/Dalamud/Game/Agent/AgentArgTypes/AgentClassJobChangeArgs.cs b/Dalamud/Game/Agent/AgentArgTypes/AgentClassJobChangeArgs.cs deleted file mode 100644 index 351760963..000000000 --- a/Dalamud/Game/Agent/AgentArgTypes/AgentClassJobChangeArgs.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Dalamud.Game.Agent.AgentArgTypes; - -/// -/// Agent argument data for game events. -/// -public class AgentClassJobChangeArgs : AgentArgs -{ - /// - /// Initializes a new instance of the class. - /// - internal AgentClassJobChangeArgs() - { - } - - /// - public override AgentArgsType Type => AgentArgsType.ClassJobChange; - - /// - /// Gets or sets a value indicating what the new ClassJob is. - /// - public byte ClassJobId { get; set; } -} diff --git a/Dalamud/Game/Agent/AgentArgTypes/AgentGameEventArgs.cs b/Dalamud/Game/Agent/AgentArgTypes/AgentGameEventArgs.cs deleted file mode 100644 index 3da601707..000000000 --- a/Dalamud/Game/Agent/AgentArgTypes/AgentGameEventArgs.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Dalamud.Game.Agent.AgentArgTypes; - -/// -/// Agent argument data for game events. -/// -public class AgentGameEventArgs : AgentArgs -{ - /// - /// Initializes a new instance of the class. - /// - internal AgentGameEventArgs() - { - } - - /// - public override AgentArgsType Type => AgentArgsType.GameEvent; - - /// - /// Gets or sets a value representing which gameEvent was triggered. - /// - public int GameEvent { get; set; } -} diff --git a/Dalamud/Game/Agent/AgentArgTypes/AgentLevelChangeArgs.cs b/Dalamud/Game/Agent/AgentArgTypes/AgentLevelChangeArgs.cs deleted file mode 100644 index a74371ebb..000000000 --- a/Dalamud/Game/Agent/AgentArgTypes/AgentLevelChangeArgs.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Dalamud.Game.Agent.AgentArgTypes; - -/// -/// Agent argument data for game events. -/// -public class AgentLevelChangeArgs : AgentArgs -{ - /// - /// Initializes a new instance of the class. - /// - internal AgentLevelChangeArgs() - { - } - - /// - public override AgentArgsType Type => AgentArgsType.LevelChange; - - /// - /// Gets or sets a value indicating which ClassJob was switched to. - /// - public byte ClassJobId { get; set; } - - /// - /// Gets or sets a value indicating what the new level is. - /// - public ushort Level { get; set; } -} diff --git a/Dalamud/Game/Agent/AgentArgTypes/AgentReceiveEventArgs.cs b/Dalamud/Game/Agent/AgentArgTypes/AgentReceiveEventArgs.cs deleted file mode 100644 index 01e1f25f6..000000000 --- a/Dalamud/Game/Agent/AgentArgTypes/AgentReceiveEventArgs.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Dalamud.Game.Agent.AgentArgTypes; - -/// -/// Agent argument data for ReceiveEvent events. -/// -public class AgentReceiveEventArgs : AgentArgs -{ - /// - /// Initializes a new instance of the class. - /// - internal AgentReceiveEventArgs() - { - } - - /// - public override AgentArgsType Type => AgentArgsType.ReceiveEvent; - - /// - /// Gets or sets the AtkValue return value for this event message. - /// - public nint ReturnValue { get; set; } - - /// - /// Gets or sets the AtkValue array for this event message. - /// - public nint AtkValues { get; set; } - - /// - /// Gets or sets the AtkValue count for this event message. - /// - public uint ValueCount { get; set; } - - /// - /// Gets or sets the event kind for this event message. - /// - public ulong EventKind { get; set; } -} diff --git a/Dalamud/Game/Agent/AgentArgsType.cs b/Dalamud/Game/Agent/AgentArgsType.cs deleted file mode 100644 index 0c96c0135..000000000 --- a/Dalamud/Game/Agent/AgentArgsType.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Dalamud.Game.Agent; - -/// -/// Enumeration for available AgentLifecycle arg data. -/// -public enum AgentArgsType -{ - /// - /// Generic arg type that contains no meaningful data. - /// - Generic, - - /// - /// Contains argument data for ReceiveEvent. - /// - ReceiveEvent, - - /// - /// Contains argument data for GameEvent. - /// - GameEvent, - - /// - /// Contains argument data for LevelChange. - /// - LevelChange, - - /// - /// Contains argument data for ClassJobChange. - /// - ClassJobChange, -} diff --git a/Dalamud/Game/Agent/AgentEvent.cs b/Dalamud/Game/Agent/AgentEvent.cs deleted file mode 100644 index e9c9a1b85..000000000 --- a/Dalamud/Game/Agent/AgentEvent.cs +++ /dev/null @@ -1,87 +0,0 @@ -namespace Dalamud.Game.Agent; - -/// -/// Enumeration for available AgentLifecycle events. -/// -public enum AgentEvent -{ - /// - /// An event that is fired before the agent processes its Receive Event Function. - /// - PreReceiveEvent, - - /// - /// An event that is fired after the agent has processed its Receive Event Function. - /// - PostReceiveEvent, - - /// - /// An event that is fired before the agent processes its Filtered Receive Event Function. - /// - PreReceiveEventWithResult, - - /// - /// An event that is fired after the agent has processed its Filtered Receive Event Function. - /// - PostReceiveEventWithResult, - - /// - /// An event that is fired before the agent processes its Show Function. - /// - PreShow, - - /// - /// An event that is fired after the agent has processed its Show Function. - /// - PostShow, - - /// - /// An event that is fired before the agent processes its Hide Function. - /// - PreHide, - - /// - /// An event that is fired after the agent has processed its Hide Function. - /// - PostHide, - - /// - /// An event that is fired before the agent processes its Update Function. - /// - PreUpdate, - - /// - /// An event that is fired after the agent has processed its Update Function. - /// - PostUpdate, - - /// - /// An event that is fired before the agent processes its Game Event Function. - /// - PreGameEvent, - - /// - /// An event that is fired after the agent has processed its Game Event Function. - /// - PostGameEvent, - - /// - /// An event that is fired before the agent processes its Game Event Function. - /// - PreLevelChange, - - /// - /// An event that is fired after the agent has processed its Level Change Function. - /// - PostLevelChange, - - /// - /// An event that is fired before the agent processes its ClassJob Change Function. - /// - PreClassJobChange, - - /// - /// An event that is fired after the agent has processed its ClassJob Change Function. - /// - PostClassJobChange, -} diff --git a/Dalamud/Game/Agent/AgentLifecycle.cs b/Dalamud/Game/Agent/AgentLifecycle.cs deleted file mode 100644 index 1c895f9da..000000000 --- a/Dalamud/Game/Agent/AgentLifecycle.cs +++ /dev/null @@ -1,344 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; - -using Dalamud.Game.Agent.AgentArgTypes; -using Dalamud.Hooking; -using Dalamud.IoC; -using Dalamud.IoC.Internal; -using Dalamud.Logging.Internal; -using Dalamud.Plugin.Services; - -using FFXIVClientStructs.FFXIV.Client.UI; -using FFXIVClientStructs.FFXIV.Client.UI.Agent; -using FFXIVClientStructs.Interop; - -namespace Dalamud.Game.Agent; - -/// -/// This class provides events for in-game agent lifecycles. -/// -[ServiceManager.EarlyLoadedService] -internal unsafe class AgentLifecycle : IInternalDisposableService -{ - /// - /// Gets a list of all allocated agent virtual tables. - /// - public static readonly List AllocatedTables = []; - - private static readonly ModuleLog Log = new("AgentLifecycle"); - - [ServiceManager.ServiceDependency] - private readonly Framework framework = Service.Get(); - - private Hook? onInitializeAgentsHook; - private bool isInvokingListeners; - - [ServiceManager.ServiceConstructor] - private AgentLifecycle() - { - var agentModuleInstance = AgentModule.Instance(); - - // Hook is only used to determine appropriate timing for replacing Agent Virtual Tables - // If the agent module is already initialized, then we can replace the tables safely. - if (agentModuleInstance is null) - { - this.onInitializeAgentsHook = Hook.FromAddress((nint)AgentModule.MemberFunctionPointers.Ctor, this.OnAgentModuleInitialize); - this.onInitializeAgentsHook.Enable(); - } - else - { - // For safety because this might be injected async, we will make sure we are on the main thread first. - this.framework.RunOnFrameworkThread(() => this.ReplaceVirtualTables(agentModuleInstance)); - } - } - - /// - /// Gets a list of all AgentLifecycle Event Listeners. - ///
- /// Mapping is: EventType -> ListenerList - internal Dictionary>> EventListeners { get; } = []; - - /// - void IInternalDisposableService.DisposeService() - { - this.onInitializeAgentsHook?.Dispose(); - this.onInitializeAgentsHook = null; - - AllocatedTables.ForEach(entry => entry.Dispose()); - AllocatedTables.Clear(); - } - - /// - /// Resolves a virtual table address to the original virtual table address. - /// - /// The modified address to resolve. - /// The original address. - internal static AgentInterface.AgentInterfaceVirtualTable* GetOriginalVirtualTable(AgentInterface.AgentInterfaceVirtualTable* tableAddress) - { - var matchedTable = AllocatedTables.FirstOrDefault(table => table.ModifiedVirtualTable == tableAddress); - if (matchedTable == null) - { - return null; - } - - return matchedTable.OriginalVirtualTable; - } - - /// - /// Register a listener for the target event and agent. - /// - /// The listener to register. - internal void RegisterListener(AgentLifecycleEventListener listener) - { - if (this.isInvokingListeners) - { - this.framework.RunOnTick(() => this.RegisterListenerMethod(listener)); - } - else - { - this.framework.RunOnFrameworkThread(() => this.RegisterListenerMethod(listener)); - } - } - - /// - /// Unregisters the listener from events. - /// - /// The listener to unregister. - internal void UnregisterListener(AgentLifecycleEventListener listener) - { - listener.IsRequestedToClear = true; - - if (this.isInvokingListeners) - { - this.framework.RunOnTick(() => this.UnregisterListenerMethod(listener)); - } - else - { - this.framework.RunOnFrameworkThread(() => this.UnregisterListenerMethod(listener)); - } - } - - /// - /// Invoke listeners for the specified event type. - /// - /// Event Type. - /// AgentARgs. - /// What to blame on errors. - internal void InvokeListenersSafely(AgentEvent eventType, AgentArgs args, [CallerMemberName] string blame = "") - { - this.isInvokingListeners = true; - - // Early return if we don't have any listeners of this type - if (!this.EventListeners.TryGetValue(eventType, out var agentListeners)) return; - - // Handle listeners for this event type that don't care which agent is triggering it - if (agentListeners.TryGetValue((AgentId)uint.MaxValue, out var globalListeners)) - { - foreach (var listener in globalListeners) - { - if (listener.IsRequestedToClear) continue; - - try - { - listener.FunctionDelegate.Invoke(eventType, args); - } - catch (Exception e) - { - Log.Error(e, $"Exception in {blame} during {eventType} invoke, for global agent event listener."); - } - } - } - - // Handle listeners that are listening for this agent and event type specifically - if (agentListeners.TryGetValue(args.AgentId, out var agentListener)) - { - foreach (var listener in agentListener) - { - if (listener.IsRequestedToClear) continue; - - try - { - listener.FunctionDelegate.Invoke(eventType, args); - } - catch (Exception e) - { - Log.Error(e, $"Exception in {blame} during {eventType} invoke, for specific agent {args.AgentId}."); - } - } - } - - this.isInvokingListeners = false; - } - - private void OnAgentModuleInitialize(AgentModule* thisPtr, UIModule* uiModule) - { - this.onInitializeAgentsHook!.Original(thisPtr, uiModule); - - try - { - this.ReplaceVirtualTables(thisPtr); - - // We don't need this hook anymore, it did its job! - this.onInitializeAgentsHook!.Dispose(); - this.onInitializeAgentsHook = null; - } - catch (Exception e) - { - Log.Error(e, "Exception in AgentLifecycle during AgentModule Ctor."); - } - } - - private void RegisterListenerMethod(AgentLifecycleEventListener listener) - { - if (!this.EventListeners.ContainsKey(listener.EventType)) - { - if (!this.EventListeners.TryAdd(listener.EventType, [])) - { - return; - } - } - - // Note: uint.MaxValue is a valid agent id, as that will trigger on any agent for this event type - if (!this.EventListeners[listener.EventType].ContainsKey(listener.AgentId)) - { - if (!this.EventListeners[listener.EventType].TryAdd(listener.AgentId, [])) - { - return; - } - } - - this.EventListeners[listener.EventType][listener.AgentId].Add(listener); - } - - private void UnregisterListenerMethod(AgentLifecycleEventListener listener) - { - if (this.EventListeners.TryGetValue(listener.EventType, out var agentListeners)) - { - if (agentListeners.TryGetValue(listener.AgentId, out var agentListener)) - { - agentListener.Remove(listener); - } - } - } - - private void ReplaceVirtualTables(AgentModule* agentModule) - { - foreach (uint index in Enumerable.Range(0, agentModule->Agents.Length)) - { - try - { - var agentPointer = agentModule->Agents.GetPointer((int)index); - - if (agentPointer is null) - { - Log.Warning("Null Agent Found?"); - continue; - } - - // AgentVirtualTable class handles creating the virtual table, and overriding each of the tracked virtual functions - AllocatedTables.Add(new AgentVirtualTable(agentPointer->Value, (AgentId)index, this)); - } - catch (Exception e) - { - Log.Error(e, "Exception in AgentLifecycle during ReplaceVirtualTables."); - } - } - } -} - -/// -/// Plugin-scoped version of a AgentLifecycle service. -/// -[PluginInterface] -[ServiceManager.ScopedService] -#pragma warning disable SA1015 -[ResolveVia] -#pragma warning restore SA1015 -internal class AgentLifecyclePluginScoped : IInternalDisposableService, IAgentLifecycle -{ - [ServiceManager.ServiceDependency] - private readonly AgentLifecycle agentLifecycleService = Service.Get(); - - private readonly List eventListeners = []; - - /// - void IInternalDisposableService.DisposeService() - { - foreach (var listener in this.eventListeners) - { - this.agentLifecycleService.UnregisterListener(listener); - } - } - - /// - public void RegisterListener(AgentEvent eventType, IEnumerable agentIds, IAgentLifecycle.AgentEventDelegate handler) - { - foreach (var agentId in agentIds) - { - this.RegisterListener(eventType, agentId, handler); - } - } - - /// - public void RegisterListener(AgentEvent eventType, AgentId agentId, IAgentLifecycle.AgentEventDelegate handler) - { - var listener = new AgentLifecycleEventListener(eventType, agentId, handler); - this.eventListeners.Add(listener); - this.agentLifecycleService.RegisterListener(listener); - } - - /// - public void RegisterListener(AgentEvent eventType, IAgentLifecycle.AgentEventDelegate handler) - { - this.RegisterListener(eventType, (AgentId)uint.MaxValue, handler); - } - - /// - public void UnregisterListener(AgentEvent eventType, IEnumerable agentIds, IAgentLifecycle.AgentEventDelegate? handler = null) - { - foreach (var agentId in agentIds) - { - this.UnregisterListener(eventType, agentId, handler); - } - } - - /// - public void UnregisterListener(AgentEvent eventType, AgentId agentId, IAgentLifecycle.AgentEventDelegate? handler = null) - { - this.eventListeners.RemoveAll(entry => - { - if (entry.EventType != eventType) return false; - if (entry.AgentId != agentId) return false; - if (handler is not null && entry.FunctionDelegate != handler) return false; - - this.agentLifecycleService.UnregisterListener(entry); - return true; - }); - } - - /// - public void UnregisterListener(AgentEvent eventType, IAgentLifecycle.AgentEventDelegate? handler = null) - { - this.UnregisterListener(eventType, (AgentId)uint.MaxValue, handler); - } - - /// - public void UnregisterListener(params IAgentLifecycle.AgentEventDelegate[] handlers) - { - foreach (var handler in handlers) - { - this.eventListeners.RemoveAll(entry => - { - if (entry.FunctionDelegate != handler) return false; - - this.agentLifecycleService.UnregisterListener(entry); - return true; - }); - } - } - - /// - public unsafe nint GetOriginalVirtualTable(nint virtualTableAddress) - => (nint)AgentLifecycle.GetOriginalVirtualTable((AgentInterface.AgentInterfaceVirtualTable*)virtualTableAddress); -} diff --git a/Dalamud/Game/Agent/AgentLifecycleEventListener.cs b/Dalamud/Game/Agent/AgentLifecycleEventListener.cs deleted file mode 100644 index 592c126ba..000000000 --- a/Dalamud/Game/Agent/AgentLifecycleEventListener.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Dalamud.Plugin.Services; - -namespace Dalamud.Game.Agent; - -/// -/// This class is a helper for tracking and invoking listener delegates. -/// -public class AgentLifecycleEventListener -{ - /// - /// Initializes a new instance of the class. - /// - /// Event type to listen for. - /// Agent id to listen for. - /// Delegate to invoke. - internal AgentLifecycleEventListener(AgentEvent eventType, AgentId agentId, IAgentLifecycle.AgentEventDelegate functionDelegate) - { - this.EventType = eventType; - this.AgentId = agentId; - this.FunctionDelegate = functionDelegate; - } - - /// - /// Gets the agentId of the agent this listener is looking for. - /// uint.MaxValue if it wants to be called for any agent. - /// - public AgentId AgentId { get; init; } - - /// - /// Gets the event type this listener is looking for. - /// - public AgentEvent EventType { get; init; } - - /// - /// Gets the delegate this listener invokes. - /// - public IAgentLifecycle.AgentEventDelegate FunctionDelegate { get; init; } - - /// - /// Gets or sets if the listener is requested to be cleared. - /// - internal bool IsRequestedToClear { get; set; } -} diff --git a/Dalamud/Game/Agent/AgentVirtualTable.cs b/Dalamud/Game/Agent/AgentVirtualTable.cs deleted file mode 100644 index 99f613137..000000000 --- a/Dalamud/Game/Agent/AgentVirtualTable.cs +++ /dev/null @@ -1,391 +0,0 @@ -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; - -using Dalamud.Game.Agent.AgentArgTypes; -using Dalamud.Logging.Internal; - -using FFXIVClientStructs.FFXIV.Client.System.Memory; -using FFXIVClientStructs.FFXIV.Client.UI.Agent; -using FFXIVClientStructs.FFXIV.Component.GUI; - -namespace Dalamud.Game.Agent; - -/// -/// Represents a class that holds references to an agents original and modified virtual table entries. -/// -internal unsafe class AgentVirtualTable : IDisposable -{ - // This need to be at minimum the largest virtual table size of all agents - // Copying extra entries is not problematic, and is considered safe. - private const int VirtualTableEntryCount = 60; - - private const bool EnableLogging = false; - - private static readonly ModuleLog Log = new("AgentVT"); - - private readonly AgentLifecycle lifecycleService; - - private readonly AgentId agentId; - - // Each agent gets its own set of args that are used to mutate the original call when used in pre-calls - private readonly AgentReceiveEventArgs receiveEventArgs = new(); - private readonly AgentReceiveEventArgs filteredReceiveEventArgs = new(); - private readonly AgentArgs showArgs = new(); - private readonly AgentArgs hideArgs = new(); - private readonly AgentArgs updateArgs = new(); - private readonly AgentGameEventArgs gameEventArgs = new(); - private readonly AgentLevelChangeArgs levelChangeArgs = new(); - private readonly AgentClassJobChangeArgs classJobChangeArgs = new(); - - private readonly AgentInterface* agentInterface; - - // Pinned Function Delegates, as these functions get assigned to an unmanaged virtual table, - // the CLR needs to know they are in use, or it will invalidate them causing random crashing. - private readonly AgentInterface.Delegates.ReceiveEvent receiveEventFunction; - private readonly AgentInterface.Delegates.ReceiveEventWithResult receiveEventWithResultFunction; - private readonly AgentInterface.Delegates.Show showFunction; - private readonly AgentInterface.Delegates.Hide hideFunction; - private readonly AgentInterface.Delegates.Update updateFunction; - private readonly AgentInterface.Delegates.OnGameEvent gameEventFunction; - private readonly AgentInterface.Delegates.OnLevelChange levelChangeFunction; - private readonly AgentInterface.Delegates.OnClassJobChange classJobChangeFunction; - - /// - /// Initializes a new instance of the class. - /// - /// AgentInterface* for the agent to replace the table of. - /// Agent ID. - /// Reference to AgentLifecycle service to callback and invoke listeners. - internal AgentVirtualTable(AgentInterface* agent, AgentId agentId, AgentLifecycle lifecycleService) - { - this.agentInterface = agent; - this.agentId = agentId; - this.lifecycleService = lifecycleService; - - // Save original virtual table - this.OriginalVirtualTable = agent->VirtualTable; - - // Create copy of original table - // Note this will copy any derived/overriden functions that this specific agent has. - // Note: currently there are 16 virtual functions, but there's no harm in copying more for when they add new virtual functions to the game - this.ModifiedVirtualTable = (AgentInterface.AgentInterfaceVirtualTable*)IMemorySpace.GetUISpace()->Malloc(0x8 * VirtualTableEntryCount, 8); - NativeMemory.Copy(agent->VirtualTable, this.ModifiedVirtualTable, 0x8 * VirtualTableEntryCount); - - // Overwrite the agents existing virtual table with our own - agent->VirtualTable = this.ModifiedVirtualTable; - - // Pin each of our listener functions - this.receiveEventFunction = this.OnAgentReceiveEvent; - this.receiveEventWithResultFunction = this.OnAgentReceiveEventWithResult; - this.showFunction = this.OnAgentShow; - this.hideFunction = this.OnAgentHide; - this.updateFunction = this.OnAgentUpdate; - this.gameEventFunction = this.OnAgentGameEvent; - this.levelChangeFunction = this.OnAgentLevelChange; - this.classJobChangeFunction = this.OnClassJobChange; - - // Overwrite specific virtual table entries - this.ModifiedVirtualTable->ReceiveEvent = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.receiveEventFunction); - this.ModifiedVirtualTable->ReceiveEventWithResult = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.receiveEventWithResultFunction); - this.ModifiedVirtualTable->Show = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.showFunction); - this.ModifiedVirtualTable->Hide = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.hideFunction); - this.ModifiedVirtualTable->Update = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.updateFunction); - this.ModifiedVirtualTable->OnGameEvent = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.gameEventFunction); - this.ModifiedVirtualTable->OnLevelChange = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.levelChangeFunction); - this.ModifiedVirtualTable->OnClassJobChange = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.classJobChangeFunction); - } - - /// - /// Gets the original virtual table address for this agent. - /// - internal AgentInterface.AgentInterfaceVirtualTable* OriginalVirtualTable { get; private set; } - - /// - /// Gets the modified virtual address for this agent. - /// - internal AgentInterface.AgentInterfaceVirtualTable* ModifiedVirtualTable { get; private set; } - - /// - public void Dispose() - { - // Ensure restoration is done atomically. - Interlocked.Exchange(ref *(nint*)&this.agentInterface->VirtualTable, (nint)this.OriginalVirtualTable); - IMemorySpace.Free(this.ModifiedVirtualTable, 0x8 * VirtualTableEntryCount); - } - - private AtkValue* OnAgentReceiveEvent(AgentInterface* thisPtr, AtkValue* returnValue, AtkValue* values, uint valueCount, ulong eventKind) - { - AtkValue* result = null; - - try - { - this.LogEvent(EnableLogging); - - this.receiveEventArgs.Agent = thisPtr; - this.receiveEventArgs.AgentId = this.agentId; - this.receiveEventArgs.ReturnValue = (nint)returnValue; - this.receiveEventArgs.AtkValues = (nint)values; - this.receiveEventArgs.ValueCount = valueCount; - this.receiveEventArgs.EventKind = eventKind; - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PreReceiveEvent, this.receiveEventArgs); - - returnValue = (AtkValue*)this.receiveEventArgs.ReturnValue; - values = (AtkValue*)this.receiveEventArgs.AtkValues; - valueCount = this.receiveEventArgs.ValueCount; - eventKind = this.receiveEventArgs.EventKind; - - try - { - result = this.OriginalVirtualTable->ReceiveEvent(thisPtr, returnValue, values, valueCount, eventKind); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Agent ReceiveEvent. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PostReceiveEvent, this.receiveEventArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAgentReceiveEvent."); - } - - return result; - } - - private AtkValue* OnAgentReceiveEventWithResult(AgentInterface* thisPtr, AtkValue* returnValue, AtkValue* values, uint valueCount, ulong eventKind) - { - AtkValue* result = null; - - try - { - this.LogEvent(EnableLogging); - - this.filteredReceiveEventArgs.Agent = thisPtr; - this.filteredReceiveEventArgs.AgentId = this.agentId; - this.filteredReceiveEventArgs.ReturnValue = (nint)returnValue; - this.filteredReceiveEventArgs.AtkValues = (nint)values; - this.filteredReceiveEventArgs.ValueCount = valueCount; - this.filteredReceiveEventArgs.EventKind = eventKind; - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PreReceiveEventWithResult, this.filteredReceiveEventArgs); - - returnValue = (AtkValue*)this.filteredReceiveEventArgs.ReturnValue; - values = (AtkValue*)this.filteredReceiveEventArgs.AtkValues; - valueCount = this.filteredReceiveEventArgs.ValueCount; - eventKind = this.filteredReceiveEventArgs.EventKind; - - try - { - result = this.OriginalVirtualTable->ReceiveEventWithResult(thisPtr, returnValue, values, valueCount, eventKind); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Agent FilteredReceiveEvent. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PostReceiveEventWithResult, this.filteredReceiveEventArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAgentReceiveEventWithResult."); - } - - return result; - } - - private void OnAgentShow(AgentInterface* thisPtr) - { - try - { - this.LogEvent(EnableLogging); - - this.showArgs.Agent = thisPtr; - this.showArgs.AgentId = this.agentId; - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PreShow, this.showArgs); - - try - { - this.OriginalVirtualTable->Show(thisPtr); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon Show. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PostShow, this.showArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAgentShow."); - } - } - - private void OnAgentHide(AgentInterface* thisPtr) - { - try - { - this.LogEvent(EnableLogging); - - this.hideArgs.Agent = thisPtr; - this.hideArgs.AgentId = this.agentId; - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PreHide, this.hideArgs); - - try - { - this.OriginalVirtualTable->Hide(thisPtr); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon Hide. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PostHide, this.hideArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAgentHide."); - } - } - - private void OnAgentUpdate(AgentInterface* thisPtr, uint frameCount) - { - try - { - this.LogEvent(EnableLogging); - - this.updateArgs.Agent = thisPtr; - this.updateArgs.AgentId = this.agentId; - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PreUpdate, this.updateArgs); - - try - { - this.OriginalVirtualTable->Update(thisPtr, frameCount); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon Update. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PostUpdate, this.updateArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAgentUpdate."); - } - } - - private void OnAgentGameEvent(AgentInterface* thisPtr, AgentInterface.GameEvent gameEvent) - { - try - { - this.LogEvent(EnableLogging); - - this.gameEventArgs.Agent = thisPtr; - this.gameEventArgs.AgentId = this.agentId; - this.gameEventArgs.GameEvent = (int)gameEvent; - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PreGameEvent, this.gameEventArgs); - - gameEvent = (AgentInterface.GameEvent)this.gameEventArgs.GameEvent; - - try - { - this.OriginalVirtualTable->OnGameEvent(thisPtr, gameEvent); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon OnGameEvent. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PostGameEvent, this.gameEventArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAgentGameEvent."); - } - } - - private void OnAgentLevelChange(AgentInterface* thisPtr, byte classJobId, ushort level) - { - try - { - this.LogEvent(EnableLogging); - - this.levelChangeArgs.Agent = thisPtr; - this.levelChangeArgs.AgentId = this.agentId; - this.levelChangeArgs.ClassJobId = classJobId; - this.levelChangeArgs.Level = level; - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PreLevelChange, this.levelChangeArgs); - - classJobId = this.levelChangeArgs.ClassJobId; - level = this.levelChangeArgs.Level; - - try - { - this.OriginalVirtualTable->OnLevelChange(thisPtr, classJobId, level); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon OnLevelChange. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PostLevelChange, this.levelChangeArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnAgentLevelChange."); - } - } - - private void OnClassJobChange(AgentInterface* thisPtr, byte classJobId) - { - try - { - this.LogEvent(EnableLogging); - - this.classJobChangeArgs.Agent = thisPtr; - this.classJobChangeArgs.AgentId = this.agentId; - this.classJobChangeArgs.ClassJobId = classJobId; - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PreClassJobChange, this.classJobChangeArgs); - - classJobId = this.classJobChangeArgs.ClassJobId; - - try - { - this.OriginalVirtualTable->OnClassJobChange(thisPtr, classJobId); - } - catch (Exception e) - { - Log.Error(e, "Caught exception when calling original Addon OnClassJobChange. This may be a bug in the game or another plugin hooking this method."); - } - - this.lifecycleService.InvokeListenersSafely(AgentEvent.PostClassJobChange, this.classJobChangeArgs); - } - catch (Exception e) - { - Log.Error(e, "Caught exception from Dalamud when attempting to process OnClassJobChange."); - } - } - - [Conditional("DEBUG")] - private void LogEvent(bool loggingEnabled, [CallerMemberName] string caller = "") - { - if (loggingEnabled) - { - // Manually disable the really spammy log events, you can comment this out if you need to debug them. - if (caller is "OnAgentUpdate" || this.agentId is AgentId.PadMouseMode) - return; - - Log.Debug($"[{caller}]: {this.agentId}"); - } - } -} diff --git a/Dalamud/Game/BaseAddressResolver.cs b/Dalamud/Game/BaseAddressResolver.cs index 1f20e3aeb..4133117d7 100644 --- a/Dalamud/Game/BaseAddressResolver.cs +++ b/Dalamud/Game/BaseAddressResolver.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; -using Dalamud.Plugin.Services; - namespace Dalamud.Game; /// @@ -14,7 +12,7 @@ public abstract class BaseAddressResolver /// /// Gets a list of memory addresses that were found, to list in /xldata. /// - public static Dictionary> DebugScannedValues { get; } = []; + public static Dictionary> DebugScannedValues { get; } = new(); /// /// Gets or sets a value indicating whether the resolver has successfully run or . diff --git a/Dalamud/Game/Chat/LogMessage.cs b/Dalamud/Game/Chat/LogMessage.cs deleted file mode 100644 index c772783a1..000000000 --- a/Dalamud/Game/Chat/LogMessage.cs +++ /dev/null @@ -1,221 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -using Dalamud.Data; -using Dalamud.Utility; - -using FFXIVClientStructs.FFXIV.Client.System.String; -using FFXIVClientStructs.FFXIV.Client.UI.Agent; -using FFXIVClientStructs.FFXIV.Client.UI.Misc; -using FFXIVClientStructs.FFXIV.Component.Text; -using FFXIVClientStructs.Interop; - -using Lumina.Excel; -using Lumina.Text.ReadOnly; - -namespace Dalamud.Game.Chat; - -/// -/// Interface representing a log message. -/// -public interface ILogMessage : IEquatable -{ - /// - /// Gets the address of the log message in memory. - /// - nint Address { get; } - - /// - /// Gets the ID of this log message. - /// - uint LogMessageId { get; } - - /// - /// Gets the GameData associated with this log message. - /// - RowRef GameData { get; } - - /// - /// Gets the entity that is the source of this log message, if any. - /// - ILogMessageEntity? SourceEntity { get; } - - /// - /// Gets the entity that is the target of this log message, if any. - /// - ILogMessageEntity? TargetEntity { get; } - - /// - /// Gets the number of parameters. - /// - int ParameterCount { get; } - - /// - /// Retrieves the value of a parameter for the log message if it is an int. - /// - /// The index of the parameter to retrieve. - /// The value of the parameter. - /// if the parameter was retrieved successfully. - bool TryGetIntParameter(int index, out int value); - - /// - /// Retrieves the value of a parameter for the log message if it is a string. - /// - /// The index of the parameter to retrieve. - /// The value of the parameter. - /// if the parameter was retrieved successfully. - bool TryGetStringParameter(int index, out ReadOnlySeString value); - - /// - /// Formats this log message into an approximation of the string that will eventually be shown in the log. - /// - /// This can cause side effects such as playing sound effects and thus should only be used for debugging. - /// The formatted string. - ReadOnlySeString FormatLogMessageForDebugging(); -} - -/// -/// This struct represents log message in the queue to be added to the chat. -/// -/// A pointer to the log message. -internal unsafe readonly struct LogMessage(LogMessageQueueItem* ptr) : ILogMessage -{ - /// - public nint Address => (nint)ptr; - - /// - public uint LogMessageId => ptr->LogMessageId; - - /// - public RowRef GameData => LuminaUtils.CreateRef(ptr->LogMessageId); - - /// - ILogMessageEntity? ILogMessage.SourceEntity => ptr->SourceKind == EntityRelationKind.None ? null : this.SourceEntity; - - /// - ILogMessageEntity? ILogMessage.TargetEntity => ptr->TargetKind == EntityRelationKind.None ? null : this.TargetEntity; - - /// - public int ParameterCount => ptr->Parameters.Count; - - private LogMessageEntity SourceEntity => new(ptr, true); - - private LogMessageEntity TargetEntity => new(ptr, false); - - public static bool operator ==(LogMessage x, LogMessage y) => x.Equals(y); - - public static bool operator !=(LogMessage x, LogMessage y) => !(x == y); - - /// - public bool Equals(ILogMessage? other) - { - return other is LogMessage logMessage && this.Equals(logMessage); - } - - /// - public override bool Equals([NotNullWhen(true)] object? obj) - { - return obj is LogMessage logMessage && this.Equals(logMessage); - } - - /// - public override int GetHashCode() - { - return HashCode.Combine(this.LogMessageId, this.SourceEntity, this.TargetEntity); - } - - /// - public bool TryGetIntParameter(int index, out int value) - { - value = 0; - if (!this.TryGetParameter(index, out var parameter)) return false; - if (parameter.Type != TextParameterType.Integer) return false; - value = parameter.IntValue; - return true; - } - - /// - public bool TryGetStringParameter(int index, out ReadOnlySeString value) - { - value = default; - if (!this.TryGetParameter(index, out var parameter)) return false; - if (parameter.Type == TextParameterType.String) - { - value = new(parameter.StringValue.AsSpan()); - return true; - } - - if (parameter.Type == TextParameterType.ReferencedUtf8String) - { - value = new(parameter.ReferencedUtf8StringValue->Utf8String.AsSpan()); - return true; - } - - return false; - } - - /// - public ReadOnlySeString FormatLogMessageForDebugging() - { - var logModule = RaptureLogModule.Instance(); - - // the formatting logic is taken from RaptureLogModule_Update - - using var utf8 = new Utf8String(); - SetName(logModule, this.SourceEntity); - SetName(logModule, this.TargetEntity); - - using var rssb = new RentedSeStringBuilder(); - logModule->RaptureTextModule->FormatString(rssb.Builder.Append(this.GameData.Value.Text).GetViewAsSpan(), &ptr->Parameters, &utf8); - - return new ReadOnlySeString(utf8.AsSpan()); - - static void SetName(RaptureLogModule* self, LogMessageEntity item) - { - var name = item.NameSpan.GetPointer(0); - - if (item.IsPlayer) - { - var str = self->TempParseMessage.GetPointer(item.IsSourceEntity ? 8 : 9); - self->FormatPlayerLink(name, str, null, 0, item.Kind != 1 /* LocalPlayer */, item.HomeWorldId, false, null, false); - - if (item.HomeWorldId != 0 && item.HomeWorldId != AgentLobby.Instance()->LobbyData.HomeWorldId) - { - var crossWorldSymbol = self->RaptureTextModule->UnkStrings0.GetPointer(3); - if (!crossWorldSymbol->StringPtr.HasValue) - self->RaptureTextModule->ProcessMacroCode(crossWorldSymbol, "\0"u8); - str->Append(crossWorldSymbol); - if (self->UIModule->GetWorldHelper()->AllWorlds.TryGetValuePointer(item.HomeWorldId, out var world)) - str->ConcatCStr(world->Name); - } - - name = str->StringPtr; - } - - if (item.IsSourceEntity) - { - self->RaptureTextModule->SetGlobalTempEntity1(name, item.Sex, item.ObjStrId); - } - else - { - self->RaptureTextModule->SetGlobalTempEntity2(name, item.Sex, item.ObjStrId); - } - } - } - - private bool TryGetParameter(int index, out TextParameter value) - { - if (index < 0 || index >= ptr->Parameters.Count) - { - value = default; - return false; - } - - value = ptr->Parameters[index]; - return true; - } - - private bool Equals(LogMessage other) - { - return this.LogMessageId == other.LogMessageId && this.SourceEntity == other.SourceEntity && this.TargetEntity == other.TargetEntity; - } -} diff --git a/Dalamud/Game/Chat/LogMessageEntity.cs b/Dalamud/Game/Chat/LogMessageEntity.cs deleted file mode 100644 index 91e905928..000000000 --- a/Dalamud/Game/Chat/LogMessageEntity.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -using Dalamud.Data; -using Dalamud.Plugin.Services; - -using FFXIVClientStructs.FFXIV.Client.UI.Misc; - -using Lumina.Excel; -using Lumina.Excel.Sheets; -using Lumina.Text.ReadOnly; - -namespace Dalamud.Game.Chat; - -/// -/// Interface representing an entity related to a log message. -/// -public interface ILogMessageEntity : IEquatable -{ - /// - /// Gets the name of this entity. - /// - ReadOnlySeString Name { get; } - - /// - /// Gets the ID of the homeworld of this entity, if it is a player. - /// - ushort HomeWorldId { get; } - - /// - /// Gets the homeworld of this entity, if it is a player. - /// - RowRef HomeWorld { get; } - - /// - /// Gets the ObjStr ID of this entity, if not a player. See . - /// - uint ObjStrId { get; } - - /// - /// Gets a value indicating whether this entity is a player. - /// - bool IsPlayer { get; } -} - -/// -/// This struct represents an entity related to a log message. -/// -/// A pointer to the log message item. -/// If represents the source entity of the log message, otherwise represents the target entity. -internal unsafe readonly struct LogMessageEntity(LogMessageQueueItem* ptr, bool source) : ILogMessageEntity -{ - /// - public ReadOnlySeString Name => new(this.NameSpan[..this.NameSpan.IndexOf((byte)0)]); - - /// - public ushort HomeWorldId => source ? ptr->SourceHomeWorld : ptr->TargetHomeWorld; - - /// - public RowRef HomeWorld => LuminaUtils.CreateRef(this.HomeWorldId); - - /// - public uint ObjStrId => source ? ptr->SourceObjStrId : ptr->TargetObjStrId; - - /// - public bool IsPlayer => source ? ptr->SourceIsPlayer : ptr->TargetIsPlayer; - - /// - /// Gets the Span containing the raw name of this entity. - /// - internal Span NameSpan => source ? ptr->SourceName : ptr->TargetName; - - /// - /// Gets the kind of the entity. - /// - internal byte Kind => source ? (byte)ptr->SourceKind : (byte)ptr->TargetKind; - - /// - /// Gets the Sex of this entity. - /// - internal byte Sex => source ? ptr->SourceSex : ptr->TargetSex; - - /// - /// Gets a value indicating whether this entity is the source entity of a log message. - /// - internal bool IsSourceEntity => source; - - public static bool operator ==(LogMessageEntity x, LogMessageEntity y) => x.Equals(y); - - public static bool operator !=(LogMessageEntity x, LogMessageEntity y) => !(x == y); - - /// - public bool Equals(ILogMessageEntity other) - { - return other is LogMessageEntity entity && this.Equals(entity); - } - - /// - public override bool Equals([NotNullWhen(true)] object? obj) - { - return obj is LogMessageEntity entity && this.Equals(entity); - } - - /// - public override int GetHashCode() - { - return HashCode.Combine(this.Name, this.HomeWorldId, this.ObjStrId, this.Sex, this.IsPlayer); - } - - private bool Equals(LogMessageEntity other) - { - return this.Name == other.Name && this.HomeWorldId == other.HomeWorldId && this.ObjStrId == other.ObjStrId && this.Kind == other.Kind && this.Sex == other.Sex && this.IsPlayer == other.IsPlayer; - } -} diff --git a/Dalamud/Game/ChatHandlers.cs b/Dalamud/Game/ChatHandlers.cs index 2d7d3c83a..b1b798a8a 100644 --- a/Dalamud/Game/ChatHandlers.cs +++ b/Dalamud/Game/ChatHandlers.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Reflection; using System.Text.RegularExpressions; using CheapLoc; @@ -22,7 +23,7 @@ namespace Dalamud.Game; [ServiceManager.EarlyLoadedService] internal partial class ChatHandlers : IServiceType { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("ChatHandlers"); [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); @@ -103,7 +104,7 @@ internal partial class ChatHandlers : IServiceType if (this.configuration.PrintDalamudWelcomeMsg) { - chatGui.Print(string.Format(Loc.Localize("DalamudWelcome", "Dalamud {0} loaded."), Versioning.GetScmVersion()) + chatGui.Print(string.Format(Loc.Localize("DalamudWelcome", "Dalamud {0} loaded."), Util.GetScmVersion()) + string.Format(Loc.Localize("PluginsWelcome", " {0} plugin(s) loaded."), pluginManager.InstalledPlugins.Count(x => x.IsLoaded))); } @@ -115,7 +116,7 @@ internal partial class ChatHandlers : IServiceType } } - if (string.IsNullOrEmpty(this.configuration.LastVersion) || !Versioning.GetAssemblyVersion().StartsWith(this.configuration.LastVersion)) + if (string.IsNullOrEmpty(this.configuration.LastVersion) || !Util.AssemblyVersion.StartsWith(this.configuration.LastVersion)) { var linkPayload = chatGui.AddChatLinkHandler( (_, _) => dalamudInterface.OpenPluginInstallerTo(PluginInstallerOpenKind.Changelogs)); @@ -136,7 +137,7 @@ internal partial class ChatHandlers : IServiceType Type = XivChatType.Notice, }); - this.configuration.LastVersion = Versioning.GetAssemblyVersion(); + this.configuration.LastVersion = Util.AssemblyVersion; this.configuration.QueueSave(); } diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs index e0a5df06d..89dd8b8b1 100644 --- a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs +++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs @@ -63,37 +63,47 @@ public interface IAetheryteEntry } /// -/// This struct represents an aetheryte entry available to the game. +/// Class representing an aetheryte entry available to the game. /// -/// Data read from the Aetheryte List. -internal readonly struct AetheryteEntry(TeleportInfo data) : IAetheryteEntry +internal sealed class AetheryteEntry : IAetheryteEntry { - /// - public uint AetheryteId => data.AetheryteId; + private readonly TeleportInfo data; + + /// + /// Initializes a new instance of the class. + /// + /// Data read from the Aetheryte List. + internal AetheryteEntry(TeleportInfo data) + { + this.data = data; + } /// - public uint TerritoryId => data.TerritoryId; + public uint AetheryteId => this.data.AetheryteId; /// - public byte SubIndex => data.SubIndex; + public uint TerritoryId => this.data.TerritoryId; /// - public byte Ward => data.Ward; + public byte SubIndex => this.data.SubIndex; /// - public byte Plot => data.Plot; + public byte Ward => this.data.Ward; /// - public uint GilCost => data.GilCost; + public byte Plot => this.data.Plot; /// - public bool IsFavourite => data.IsFavourite; + public uint GilCost => this.data.GilCost; /// - public bool IsSharedHouse => data.IsSharedHouse; + public bool IsFavourite => this.data.IsFavourite; /// - public bool IsApartment => data.IsApartment; + public bool IsSharedHouse => this.data.IsSharedHouse; + + /// + public bool IsApartment => this.data.IsApartment; /// public RowRef AetheryteData => LuminaUtils.CreateRef(this.AetheryteId); diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs index 2df64b73b..4a6d011e9 100644 --- a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs +++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs @@ -8,7 +8,6 @@ using Dalamud.Plugin.Services; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.Game.UI; - using Serilog; namespace Dalamud.Game.ClientState.Aetherytes; @@ -89,7 +88,10 @@ internal sealed partial class AetheryteList /// public IEnumerator GetEnumerator() { - return new Enumerator(this); + for (var i = 0; i < this.Length; i++) + { + yield return this[i]; + } } /// @@ -97,34 +99,4 @@ internal sealed partial class AetheryteList { return this.GetEnumerator(); } - - private struct Enumerator(AetheryteList aetheryteList) : IEnumerator - { - private int index = -1; - - public IAetheryteEntry Current { get; private set; } - - object IEnumerator.Current => this.Current; - - public bool MoveNext() - { - if (++this.index < aetheryteList.Length) - { - this.Current = aetheryteList[this.index]; - return true; - } - - this.Current = default; - return false; - } - - public void Reset() - { - this.index = -1; - } - - public void Dispose() - { - } - } } diff --git a/Dalamud/Game/ClientState/Buddy/BuddyList.cs b/Dalamud/Game/ClientState/Buddy/BuddyList.cs index 3bec6d9f1..dbac76518 100644 --- a/Dalamud/Game/ClientState/Buddy/BuddyList.cs +++ b/Dalamud/Game/ClientState/Buddy/BuddyList.cs @@ -8,7 +8,6 @@ using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; using CSBuddy = FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy; -using CSBuddyMember = FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember; using CSUIState = FFXIVClientStructs.FFXIV.Client.Game.UI.UIState; namespace Dalamud.Game.ClientState.Buddy; @@ -24,7 +23,7 @@ namespace Dalamud.Game.ClientState.Buddy; #pragma warning restore SA1015 internal sealed partial class BuddyList : IServiceType, IBuddyList { - private const uint InvalidEntityId = 0xE0000000; + private const uint InvalidObjectID = 0xE0000000; [ServiceManager.ServiceDependency] private readonly PlayerState playerState = Service.Get(); @@ -85,37 +84,37 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList } /// - public unsafe nint GetCompanionBuddyMemberAddress() + public unsafe IntPtr GetCompanionBuddyMemberAddress() { - return (nint)this.BuddyListStruct->CompanionInfo.Companion; + return (IntPtr)this.BuddyListStruct->CompanionInfo.Companion; } /// - public unsafe nint GetPetBuddyMemberAddress() + public unsafe IntPtr GetPetBuddyMemberAddress() { - return (nint)this.BuddyListStruct->PetInfo.Pet; + return (IntPtr)this.BuddyListStruct->PetInfo.Pet; } /// - public unsafe nint GetBattleBuddyMemberAddress(int index) + public unsafe IntPtr GetBattleBuddyMemberAddress(int index) { if (index < 0 || index >= 3) - return 0; + return IntPtr.Zero; - return (nint)Unsafe.AsPointer(ref this.BuddyListStruct->BattleBuddies[index]); + return (IntPtr)Unsafe.AsPointer(ref this.BuddyListStruct->BattleBuddies[index]); } /// - public unsafe IBuddyMember? CreateBuddyMemberReference(nint address) + public IBuddyMember? CreateBuddyMemberReference(IntPtr address) { - if (address == 0) + if (address == IntPtr.Zero) return null; - if (this.playerState.ContentId == 0) + if (!this.playerState.IsLoaded) return null; - var buddy = new BuddyMember((CSBuddyMember*)address); - if (buddy.EntityId == InvalidEntityId) + var buddy = new BuddyMember(address); + if (buddy.ObjectId == InvalidObjectID) return null; return buddy; @@ -133,39 +132,12 @@ internal sealed partial class BuddyList /// public IEnumerator GetEnumerator() { - return new Enumerator(this); + for (var i = 0; i < this.Length; i++) + { + yield return this[i]; + } } /// IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); - - private struct Enumerator(BuddyList buddyList) : IEnumerator - { - private int index = -1; - - public IBuddyMember Current { get; private set; } - - object IEnumerator.Current => this.Current; - - public bool MoveNext() - { - if (++this.index < buddyList.Length) - { - this.Current = buddyList[this.index]; - return true; - } - - this.Current = default; - return false; - } - - public void Reset() - { - this.index = -1; - } - - public void Dispose() - { - } - } } diff --git a/Dalamud/Game/ClientState/Buddy/BuddyMember.cs b/Dalamud/Game/ClientState/Buddy/BuddyMember.cs index 8018bafaf..393598d32 100644 --- a/Dalamud/Game/ClientState/Buddy/BuddyMember.cs +++ b/Dalamud/Game/ClientState/Buddy/BuddyMember.cs @@ -1,24 +1,20 @@ -using System.Diagnostics.CodeAnalysis; - using Dalamud.Data; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.Types; using Lumina.Excel; -using CSBuddyMember = FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember; - namespace Dalamud.Game.ClientState.Buddy; /// /// Interface representing represents a buddy such as the chocobo companion, summoned pets, squadron groups and trust parties. /// -public interface IBuddyMember : IEquatable +public interface IBuddyMember { /// /// Gets the address of the buddy in memory. /// - nint Address { get; } + IntPtr Address { get; } /// /// Gets the object ID of this buddy. @@ -71,34 +67,42 @@ public interface IBuddyMember : IEquatable } /// -/// This struct represents a buddy such as the chocobo companion, summoned pets, squadron groups and trust parties. +/// This class represents a buddy such as the chocobo companion, summoned pets, squadron groups and trust parties. /// -/// A pointer to the BuddyMember. -internal readonly unsafe struct BuddyMember(CSBuddyMember* ptr) : IBuddyMember +internal unsafe class BuddyMember : IBuddyMember { [ServiceManager.ServiceDependency] private readonly ObjectTable objectTable = Service.Get(); - /// - public nint Address => (nint)ptr; + /// + /// Initializes a new instance of the class. + /// + /// Buddy address. + internal BuddyMember(IntPtr address) + { + this.Address = address; + } /// - public uint ObjectId => this.EntityId; + public IntPtr Address { get; } /// - public uint EntityId => ptr->EntityId; + public uint ObjectId => this.Struct->EntityId; /// - public IGameObject? GameObject => this.objectTable.SearchById(this.EntityId); + public uint EntityId => this.Struct->EntityId; /// - public uint CurrentHP => ptr->CurrentHealth; + public IGameObject? GameObject => this.objectTable.SearchById(this.ObjectId); /// - public uint MaxHP => ptr->MaxHealth; + public uint CurrentHP => this.Struct->CurrentHealth; /// - public uint DataID => ptr->DataId; + public uint MaxHP => this.Struct->MaxHealth; + + /// + public uint DataID => this.Struct->DataId; /// public RowRef MountData => LuminaUtils.CreateRef(this.DataID); @@ -109,25 +113,5 @@ internal readonly unsafe struct BuddyMember(CSBuddyMember* ptr) : IBuddyMember /// public RowRef TrustData => LuminaUtils.CreateRef(this.DataID); - public static bool operator ==(BuddyMember x, BuddyMember y) => x.Equals(y); - - public static bool operator !=(BuddyMember x, BuddyMember y) => !(x == y); - - /// - public bool Equals(IBuddyMember? other) - { - return this.EntityId == other.EntityId; - } - - /// - public override bool Equals([NotNullWhen(true)] object? obj) - { - return obj is BuddyMember fate && this.Equals(fate); - } - - /// - public override int GetHashCode() - { - return this.EntityId.GetHashCode(); - } + private FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember* Struct => (FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember*)this.Address; } diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 304dde82c..93720e1db 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -33,10 +33,11 @@ namespace Dalamud.Game.ClientState; [ServiceManager.EarlyLoadedService] internal sealed class ClientState : IInternalDisposableService, IClientState { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("ClientState"); private readonly GameLifecycle lifecycle; private readonly ClientStateAddressResolver address; + private readonly Hook handleZoneInitPacketHook; private readonly Hook uiModuleHandlePacketHook; private readonly Hook setCurrentInstanceHook; @@ -71,11 +72,13 @@ internal sealed class ClientState : IInternalDisposableService, IClientState this.ClientLanguage = (ClientLanguage)dalamud.StartInfo.Language; + this.handleZoneInitPacketHook = Hook.FromAddress(this.AddressResolver.HandleZoneInitPacket, this.HandleZoneInitPacketDetour); this.uiModuleHandlePacketHook = Hook.FromAddress((nint)UIModule.StaticVirtualTablePointer->HandlePacket, this.UIModuleHandlePacketDetour); this.setCurrentInstanceHook = Hook.FromAddress(this.AddressResolver.SetCurrentInstance, this.SetCurrentInstanceDetour); this.networkHandlers.CfPop += this.NetworkHandlersOnCfPop; + this.handleZoneInitPacketHook.Enable(); this.uiModuleHandlePacketHook.Enable(); this.setCurrentInstanceHook.Enable(); @@ -268,6 +271,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState /// void IInternalDisposableService.DisposeService() { + this.handleZoneInitPacketHook.Dispose(); this.uiModuleHandlePacketHook.Dispose(); this.onLogoutHook.Dispose(); this.setCurrentInstanceHook.Dispose(); @@ -290,6 +294,23 @@ internal sealed class ClientState : IInternalDisposableService, IClientState this.framework.Update += this.OnFrameworkUpdate; } + private void HandleZoneInitPacketDetour(nint a1, uint localPlayerEntityId, nint packet, byte type) + { + this.handleZoneInitPacketHook.Original(a1, localPlayerEntityId, packet, type); + + try + { + var eventArgs = ZoneInitEventArgs.Read(packet); + Log.Debug($"ZoneInit: {eventArgs}"); + this.ZoneInit?.InvokeSafely(eventArgs); + this.TerritoryType = (ushort)eventArgs.TerritoryType.RowId; + } + catch (Exception ex) + { + Log.Error(ex, "Exception during ZoneInit"); + } + } + private unsafe void UIModuleHandlePacketDetour( UIModule* thisPtr, UIModulePacketType type, uint uintParam, void* packet) { @@ -335,15 +356,6 @@ internal sealed class ClientState : IInternalDisposableService, IClientState break; } - - case (UIModulePacketType)5: // TODO: Use UIModulePacketType.InitZone when available - { - var eventArgs = ZoneInitEventArgs.Read((nint)packet); - Log.Debug($"ZoneInit: {eventArgs}"); - this.ZoneInit?.InvokeSafely(eventArgs); - this.TerritoryType = (ushort)eventArgs.TerritoryType.RowId; - break; - } } } diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index ae7549b97..2fc859d09 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -1,5 +1,3 @@ -using Dalamud.Plugin.Services; - namespace Dalamud.Game.ClientState; /// @@ -21,6 +19,11 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver // Functions + /// + /// Gets the address of the method that handles the ZoneInit packet. + /// + public nint HandleZoneInitPacket { get; private set; } + /// /// Gets the address of the method that sets the current public instance. /// @@ -32,6 +35,7 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver /// The signature scanner to facilitate setup. protected override void Setup64Bit(ISigScanner sig) { + this.HandleZoneInitPacket = sig.ScanText("E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 44 0F B6 45"); this.SetCurrentInstance = sig.ScanText("E8 ?? ?? ?? ?? 0F B6 55 ?? 48 8D 0D ?? ?? ?? ?? C0 EA"); // NetworkModuleProxy.SetCurrentInstance // These resolve to fixed offsets only, without the base address added in, so GetStaticAddressFromSig() can't be used. diff --git a/Dalamud/Game/ClientState/Conditions/Condition.cs b/Dalamud/Game/ClientState/Conditions/Condition.cs index 6f61ab246..99748f71b 100644 --- a/Dalamud/Game/ClientState/Conditions/Condition.cs +++ b/Dalamud/Game/ClientState/Conditions/Condition.cs @@ -18,7 +18,7 @@ internal sealed class Condition : IInternalDisposableService, ICondition /// /// Gets the current max number of conditions. You can get this just by looking at the condition sheet and how many rows it has. /// - internal const int MaxConditionEntries = 112; + internal const int MaxConditionEntries = 104; [ServiceManager.ServiceDependency] private readonly Framework framework = Service.Get(); diff --git a/Dalamud/Game/ClientState/Conditions/ConditionFlag.cs b/Dalamud/Game/ClientState/Conditions/ConditionFlag.cs index b5894d891..19451dd5c 100644 --- a/Dalamud/Game/ClientState/Conditions/ConditionFlag.cs +++ b/Dalamud/Game/ClientState/Conditions/ConditionFlag.cs @@ -520,17 +520,4 @@ public enum ConditionFlag PilotingMech = 102, // Unknown103 = 103, - - /// - /// Unable to execute command while editing a strategy board. - /// - EditingStrategyBoard = 104, - - // Unknown105 = 105, - // Unknown106 = 106, - // Unknown107 = 107, - // Unknown108 = 108, - // Unknown109 = 109, - // Unknown110 = 110, - // Unknown111 = 111, } diff --git a/Dalamud/Game/ClientState/Customize/CustomizeData.cs b/Dalamud/Game/ClientState/Customize/CustomizeData.cs deleted file mode 100644 index baf8d3a0a..000000000 --- a/Dalamud/Game/ClientState/Customize/CustomizeData.cs +++ /dev/null @@ -1,311 +0,0 @@ -using Dalamud.Game.ClientState.Objects.Types; - -namespace Dalamud.Game.ClientState.Customize; - -/// -/// This collection represents customization data a has. -/// -public interface ICustomizeData -{ - /// - /// Gets the current race. - /// E.g., Miqo'te, Aura. - /// - public byte Race { get; } - - /// - /// Gets the current sex. - /// - public byte Sex { get; } - - /// - /// Gets the current body type. - /// - public byte BodyType { get; } - - /// - /// Gets the current height (0 to 100). - /// - public byte Height { get; } - - /// - /// Gets the current tribe. - /// E.g., Seeker of the Sun, Keeper of the Moon. - /// - public byte Tribe { get; } - - /// - /// Gets the current face (1 to 4). - /// - public byte Face { get; } - - /// - /// Gets the current hairstyle. - /// - public byte Hairstyle { get; } - - /// - /// Gets the current skin color. - /// - public byte SkinColor { get; } - - /// - /// Gets the current color of the left eye. - /// - public byte EyeColorLeft { get; } - - /// - /// Gets the current color of the right eye. - /// - public byte EyeColorRight { get; } - - /// - /// Gets the current main hair color. - /// - public byte HairColor { get; } - - /// - /// Gets the current highlight hair color. - /// - public byte HighlightsColor { get; } - - /// - /// Gets the current tattoo color. - /// - public byte TattooColor { get; } - - /// - /// Gets the current eyebrow type. - /// - public byte Eyebrows { get; } - - /// - /// Gets the current nose type. - /// - public byte Nose { get; } - - /// - /// Gets the current jaw type. - /// - public byte Jaw { get; } - - /// - /// Gets the current lip color fur pattern. - /// - public byte LipColorFurPattern { get; } - - /// - /// Gets the current muscle mass value. - /// - public byte MuscleMass { get; } - - /// - /// Gets the current tail type (1 to 4). - /// - public byte TailShape { get; } - - /// - /// Gets the current bust size (0 to 100). - /// - public byte BustSize { get; } - - /// - /// Gets the current color of the face paint. - /// - public byte FacePaintColor { get; } - - /// - /// Gets a value indicating whether highlight color is used. - /// - public bool Highlights { get; } - - /// - /// Gets a value indicating whether this facial feature is used. - /// - public bool FacialFeature1 { get; } - - /// - public bool FacialFeature2 { get; } - - /// - public bool FacialFeature3 { get; } - - /// - public bool FacialFeature4 { get; } - - /// - public bool FacialFeature5 { get; } - - /// - public bool FacialFeature6 { get; } - - /// - public bool FacialFeature7 { get; } - - /// - /// Gets a value indicating whether the legacy tattoo is used. - /// - public bool LegacyTattoo { get; } - - /// - /// Gets the current eye shape type. - /// - public byte EyeShape { get; } - - /// - /// Gets a value indicating whether small iris is used. - /// - public bool SmallIris { get; } - - /// - /// Gets the current mouth type. - /// - public byte Mouth { get; } - - /// - /// Gets a value indicating whether lipstick is used. - /// - public bool Lipstick { get; } - - /// - /// Gets the current face paint type. - /// - public byte FacePaint { get; } - - /// - /// Gets a value indicating whether face paint reversed is used. - /// - public bool FacePaintReversed { get; } -} - -/// -internal readonly unsafe struct CustomizeData : ICustomizeData -{ - /// - /// Gets or sets the address of the customize data struct in memory. - /// - public readonly nint Address; - - /// - /// Initializes a new instance of the struct. - /// - /// Address of the status list. - internal CustomizeData(nint address) - { - this.Address = address; - } - - /// - public byte Race => this.Struct->Race; - - /// - public byte Sex => this.Struct->Sex; - - /// - public byte BodyType => this.Struct->BodyType; - - /// - public byte Height => this.Struct->Height; - - /// - public byte Tribe => this.Struct->Tribe; - - /// - public byte Face => this.Struct->Face; - - /// - public byte Hairstyle => this.Struct->Hairstyle; - - /// - public byte SkinColor => this.Struct->SkinColor; - - /// - public byte EyeColorLeft => this.Struct->EyeColorLeft; - - /// - public byte EyeColorRight => this.Struct->EyeColorRight; - - /// - public byte HairColor => this.Struct->HairColor; - - /// - public byte HighlightsColor => this.Struct->HighlightsColor; - - /// - public byte TattooColor => this.Struct->TattooColor; - - /// - public byte Eyebrows => this.Struct->Eyebrows; - - /// - public byte Nose => this.Struct->Nose; - - /// - public byte Jaw => this.Struct->Jaw; - - /// - public byte LipColorFurPattern => this.Struct->LipColorFurPattern; - - /// - public byte MuscleMass => this.Struct->MuscleMass; - - /// - public byte TailShape => this.Struct->TailShape; - - /// - public byte BustSize => this.Struct->BustSize; - - /// - public byte FacePaintColor => this.Struct->FacePaintColor; - - /// - public bool Highlights => this.Struct->Highlights; - - /// - public bool FacialFeature1 => this.Struct->FacialFeature1; - - /// - public bool FacialFeature2 => this.Struct->FacialFeature2; - - /// - public bool FacialFeature3 => this.Struct->FacialFeature3; - - /// - public bool FacialFeature4 => this.Struct->FacialFeature4; - - /// - public bool FacialFeature5 => this.Struct->FacialFeature5; - - /// - public bool FacialFeature6 => this.Struct->FacialFeature6; - - /// - public bool FacialFeature7 => this.Struct->FacialFeature7; - - /// - public bool LegacyTattoo => this.Struct->LegacyTattoo; - - /// - public byte EyeShape => this.Struct->EyeShape; - - /// - public bool SmallIris => this.Struct->SmallIris; - - /// - public byte Mouth => this.Struct->Mouth; - - /// - public bool Lipstick => this.Struct->Lipstick; - - /// - public byte FacePaint => this.Struct->FacePaint; - - /// - public bool FacePaintReversed => this.Struct->FacePaintReversed; - - /// - /// Gets the underlying structure. - /// - internal FFXIVClientStructs.FFXIV.Client.Game.Character.CustomizeData* Struct => - (FFXIVClientStructs.FFXIV.Client.Game.Character.CustomizeData*)this.Address; -} diff --git a/Dalamud/Game/ClientState/Fates/Fate.cs b/Dalamud/Game/ClientState/Fates/Fate.cs index 4348c4025..f82109fd0 100644 --- a/Dalamud/Game/ClientState/Fates/Fate.cs +++ b/Dalamud/Game/ClientState/Fates/Fate.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using System.Numerics; using Dalamud.Data; @@ -8,12 +7,10 @@ using Dalamud.Memory; using Lumina.Excel; -using CSFateContext = FFXIVClientStructs.FFXIV.Client.Game.Fate.FateContext; - namespace Dalamud.Game.ClientState.Fates; /// -/// Interface representing a fate entry that can be seen in the current area. +/// Interface representing an fate entry that can be seen in the current area. /// public interface IFate : IEquatable { @@ -115,96 +112,129 @@ public interface IFate : IEquatable /// /// Gets the address of this Fate in memory. /// - nint Address { get; } + IntPtr Address { get; } } /// -/// This struct represents a Fate. +/// This class represents an FFXIV Fate. /// -/// A pointer to the FateContext. -internal readonly unsafe struct Fate(CSFateContext* ptr) : IFate +internal unsafe partial class Fate { + /// + /// Initializes a new instance of the class. + /// + /// The address of this fate in memory. + internal Fate(IntPtr address) + { + this.Address = address; + } + /// - public nint Address => (nint)ptr; + public IntPtr Address { get; } + + private FFXIVClientStructs.FFXIV.Client.Game.Fate.FateContext* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Fate.FateContext*)this.Address; + + public static bool operator ==(Fate fate1, Fate fate2) + { + if (fate1 is null || fate2 is null) + return Equals(fate1, fate2); + + return fate1.Equals(fate2); + } + + public static bool operator !=(Fate fate1, Fate fate2) => !(fate1 == fate2); + + /// + /// Gets a value indicating whether this Fate is still valid in memory. + /// + /// The fate to check. + /// True or false. + public static bool IsValid(Fate fate) + { + if (fate == null) + return false; + + var playerState = Service.Get(); + return playerState.IsLoaded == true; + } + + /// + /// Gets a value indicating whether this actor is still valid in memory. + /// + /// True or false. + public bool IsValid() => IsValid(this); /// - public ushort FateId => ptr->FateId; + bool IEquatable.Equals(IFate other) => this.FateId == other?.FateId; + + /// + public override bool Equals(object obj) => ((IEquatable)this).Equals(obj as IFate); + + /// + public override int GetHashCode() => this.FateId.GetHashCode(); +} + +/// +/// This class represents an FFXIV Fate. +/// +internal unsafe partial class Fate : IFate +{ + /// + public ushort FateId => this.Struct->FateId; /// public RowRef GameData => LuminaUtils.CreateRef(this.FateId); /// - public int StartTimeEpoch => ptr->StartTimeEpoch; + public int StartTimeEpoch => this.Struct->StartTimeEpoch; /// - public short Duration => ptr->Duration; + public short Duration => this.Struct->Duration; /// public long TimeRemaining => this.StartTimeEpoch + this.Duration - DateTimeOffset.Now.ToUnixTimeSeconds(); /// - public SeString Name => MemoryHelper.ReadSeString(&ptr->Name); + public SeString Name => MemoryHelper.ReadSeString(&this.Struct->Name); /// - public SeString Description => MemoryHelper.ReadSeString(&ptr->Description); + public SeString Description => MemoryHelper.ReadSeString(&this.Struct->Description); /// - public SeString Objective => MemoryHelper.ReadSeString(&ptr->Objective); + public SeString Objective => MemoryHelper.ReadSeString(&this.Struct->Objective); /// - public FateState State => (FateState)ptr->State; + public FateState State => (FateState)this.Struct->State; /// - public byte HandInCount => ptr->HandInCount; + public byte HandInCount => this.Struct->HandInCount; /// - public byte Progress => ptr->Progress; + public byte Progress => this.Struct->Progress; /// - public bool HasBonus => ptr->IsBonus; + public bool HasBonus => this.Struct->IsBonus; /// - public uint IconId => ptr->IconId; + public uint IconId => this.Struct->IconId; /// - public byte Level => ptr->Level; + public byte Level => this.Struct->Level; /// - public byte MaxLevel => ptr->MaxLevel; + public byte MaxLevel => this.Struct->MaxLevel; /// - public Vector3 Position => ptr->Location; + public Vector3 Position => this.Struct->Location; /// - public float Radius => ptr->Radius; + public float Radius => this.Struct->Radius; /// - public uint MapIconId => ptr->MapIconId; + public uint MapIconId => this.Struct->MapIconId; /// /// Gets the territory this is located in. /// - public RowRef TerritoryType => LuminaUtils.CreateRef(ptr->MapMarkers[0].MapMarkerData.TerritoryTypeId); - - public static bool operator ==(Fate x, Fate y) => x.Equals(y); - - public static bool operator !=(Fate x, Fate y) => !(x == y); - - /// - public bool Equals(IFate? other) - { - return this.FateId == other.FateId; - } - - /// - public override bool Equals([NotNullWhen(true)] object? obj) - { - return obj is Fate fate && this.Equals(fate); - } - - /// - public override int GetHashCode() - { - return this.FateId.GetHashCode(); - } + public RowRef TerritoryType => LuminaUtils.CreateRef(this.Struct->MapMarkers[0].MapMarkerData.TerritoryTypeId); } diff --git a/Dalamud/Game/ClientState/Fates/FateTable.cs b/Dalamud/Game/ClientState/Fates/FateTable.cs index 41e974f04..30b0f4102 100644 --- a/Dalamud/Game/ClientState/Fates/FateTable.cs +++ b/Dalamud/Game/ClientState/Fates/FateTable.cs @@ -6,7 +6,6 @@ using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; -using CSFateContext = FFXIVClientStructs.FFXIV.Client.Game.Fate.FateContext; using CSFateManager = FFXIVClientStructs.FFXIV.Client.Game.Fate.FateManager; namespace Dalamud.Game.ClientState.Fates; @@ -27,7 +26,7 @@ internal sealed partial class FateTable : IServiceType, IFateTable } /// - public unsafe nint Address => (nint)CSFateManager.Instance(); + public unsafe IntPtr Address => (nint)CSFateManager.Instance(); /// public unsafe int Length @@ -70,29 +69,29 @@ internal sealed partial class FateTable : IServiceType, IFateTable } /// - public unsafe nint GetFateAddress(int index) + public unsafe IntPtr GetFateAddress(int index) { if (index >= this.Length) - return 0; + return IntPtr.Zero; var fateManager = CSFateManager.Instance(); if (fateManager == null) - return 0; + return IntPtr.Zero; - return (nint)fateManager->Fates[index].Value; + return (IntPtr)fateManager->Fates[index].Value; } /// - public unsafe IFate? CreateFateReference(IntPtr address) + public IFate? CreateFateReference(IntPtr offset) { - if (address == 0) + if (offset == IntPtr.Zero) return null; - var clientState = Service.Get(); - if (clientState.LocalContentId == 0) + var playerState = Service.Get(); + if (!playerState.IsLoaded) return null; - return new Fate((CSFateContext*)address); + return new Fate(offset); } } @@ -107,39 +106,12 @@ internal sealed partial class FateTable /// public IEnumerator GetEnumerator() { - return new Enumerator(this); + for (var i = 0; i < this.Length; i++) + { + yield return this[i]; + } } /// IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); - - private struct Enumerator(FateTable fateTable) : IEnumerator - { - private int index = -1; - - public IFate Current { get; private set; } - - object IEnumerator.Current => this.Current; - - public bool MoveNext() - { - if (++this.index < fateTable.Length) - { - this.Current = fateTable[this.index]; - return true; - } - - this.Current = default; - return false; - } - - public void Reset() - { - this.index = -1; - } - - public void Dispose() - { - } - } } diff --git a/Dalamud/Game/ClientState/GamePad/GamepadState.cs b/Dalamud/Game/ClientState/GamePad/GamepadState.cs index 3a8642cfa..ab4f8a03f 100644 --- a/Dalamud/Game/ClientState/GamePad/GamepadState.cs +++ b/Dalamud/Game/ClientState/GamePad/GamepadState.cs @@ -5,9 +5,7 @@ using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; - using FFXIVClientStructs.FFXIV.Client.System.Input; - using Serilog; namespace Dalamud.Game.ClientState.GamePad; diff --git a/Dalamud/Game/ClientState/JobGauge/JobGauges.cs b/Dalamud/Game/ClientState/JobGauge/JobGauges.cs index 5d6ba4554..67429956b 100644 --- a/Dalamud/Game/ClientState/JobGauge/JobGauges.cs +++ b/Dalamud/Game/ClientState/JobGauge/JobGauges.cs @@ -37,7 +37,7 @@ internal class JobGauges : IServiceType, IJobGauges // Since the gauge itself reads from live memory, there isn't much downside to doing this. if (!this.cache.TryGetValue(typeof(T), out var gauge)) { - gauge = this.cache[typeof(T)] = (T)Activator.CreateInstance(typeof(T), BindingFlags.NonPublic | BindingFlags.Instance, null, [this.Address], null); + gauge = this.cache[typeof(T)] = (T)Activator.CreateInstance(typeof(T), BindingFlags.NonPublic | BindingFlags.Instance, null, new object[] { this.Address }, null); } return (T)gauge; diff --git a/Dalamud/Game/ClientState/JobGauge/Types/BRDGauge.cs b/Dalamud/Game/ClientState/JobGauge/Types/BRDGauge.cs index de73d540e..8880c3555 100644 --- a/Dalamud/Game/ClientState/JobGauge/Types/BRDGauge.cs +++ b/Dalamud/Game/ClientState/JobGauge/Types/BRDGauge.cs @@ -1,5 +1,4 @@ using Dalamud.Game.ClientState.JobGauge.Enums; - using FFXIVClientStructs.FFXIV.Client.Game.Gauge; namespace Dalamud.Game.ClientState.JobGauge.Types; @@ -83,12 +82,12 @@ public unsafe class BRDGauge : JobGaugeBaseSongFlags.HasFlag(SongFlags.MagesBalladCoda) ? Song.Mage : Song.None, this.Struct->SongFlags.HasFlag(SongFlags.ArmysPaeonCoda) ? Song.Army : Song.None, this.Struct->SongFlags.HasFlag(SongFlags.WanderersMinuetCoda) ? Song.Wanderer : Song.None, - ]; + }; } } } diff --git a/Dalamud/Game/ClientState/JobGauge/Types/DRKGauge.cs b/Dalamud/Game/ClientState/JobGauge/Types/DRKGauge.cs index 06d923cc4..c56d03db0 100644 --- a/Dalamud/Game/ClientState/JobGauge/Types/DRKGauge.cs +++ b/Dalamud/Game/ClientState/JobGauge/Types/DRKGauge.cs @@ -1,5 +1,4 @@ using Dalamud.Game.ClientState.JobGauge.Enums; - using FFXIVClientStructs.FFXIV.Client.Game.Gauge; namespace Dalamud.Game.ClientState.JobGauge.Types; diff --git a/Dalamud/Game/ClientState/JobGauge/Types/PCTGauge.cs b/Dalamud/Game/ClientState/JobGauge/Types/PCTGauge.cs index 9745bff7c..d31a22702 100644 --- a/Dalamud/Game/ClientState/JobGauge/Types/PCTGauge.cs +++ b/Dalamud/Game/ClientState/JobGauge/Types/PCTGauge.cs @@ -1,4 +1,4 @@ -using FFXIVClientStructs.FFXIV.Client.Game.Gauge; +using FFXIVClientStructs.FFXIV.Client.Game.Gauge; using CanvasFlags = Dalamud.Game.ClientState.JobGauge.Enums.CanvasFlags; using CreatureFlags = Dalamud.Game.ClientState.JobGauge.Enums.CreatureFlags; @@ -22,45 +22,45 @@ public unsafe class PCTGauge : JobGaugeBase /// /// Gets the use of subjective pallete. /// - public byte PalleteGauge => this.Struct->PalleteGauge; + public byte PalleteGauge => Struct->PalleteGauge; /// /// Gets the amount of paint the player has. /// - public byte Paint => this.Struct->Paint; + public byte Paint => Struct->Paint; /// /// Gets a value indicating whether a creature motif is drawn. /// - public bool CreatureMotifDrawn => this.Struct->CreatureMotifDrawn; + public bool CreatureMotifDrawn => Struct->CreatureMotifDrawn; /// /// Gets a value indicating whether a weapon motif is drawn. /// - public bool WeaponMotifDrawn => this.Struct->WeaponMotifDrawn; + public bool WeaponMotifDrawn => Struct->WeaponMotifDrawn; /// /// Gets a value indicating whether a landscape motif is drawn. /// - public bool LandscapeMotifDrawn => this.Struct->LandscapeMotifDrawn; + public bool LandscapeMotifDrawn => Struct->LandscapeMotifDrawn; /// /// Gets a value indicating whether a moogle portrait is ready. /// - public bool MooglePortraitReady => this.Struct->MooglePortraitReady; + public bool MooglePortraitReady => Struct->MooglePortraitReady; /// /// Gets a value indicating whether a madeen portrait is ready. /// - public bool MadeenPortraitReady => this.Struct->MadeenPortraitReady; + public bool MadeenPortraitReady => Struct->MadeenPortraitReady; /// /// Gets which creature flags are present. /// - public CreatureFlags CreatureFlags => (CreatureFlags)this.Struct->CreatureFlags; + public CreatureFlags CreatureFlags => (CreatureFlags)Struct->CreatureFlags; /// /// Gets which canvas flags are present. /// - public CanvasFlags CanvasFlags => (CanvasFlags)this.Struct->CanvasFlags; + public CanvasFlags CanvasFlags => (CanvasFlags)Struct->CanvasFlags; } diff --git a/Dalamud/Game/ClientState/JobGauge/Types/SMNGauge.cs b/Dalamud/Game/ClientState/JobGauge/Types/SMNGauge.cs index 5f2d6e932..899ea78eb 100644 --- a/Dalamud/Game/ClientState/JobGauge/Types/SMNGauge.cs +++ b/Dalamud/Game/ClientState/JobGauge/Types/SMNGauge.cs @@ -1,5 +1,4 @@ using Dalamud.Game.ClientState.JobGauge.Enums; - using FFXIVClientStructs.FFXIV.Client.Game.Gauge; namespace Dalamud.Game.ClientState.JobGauge.Types; diff --git a/Dalamud/Game/ClientState/JobGauge/Types/VPRGauge.cs b/Dalamud/Game/ClientState/JobGauge/Types/VPRGauge.cs index 625ecde24..3c822c7d7 100644 --- a/Dalamud/Game/ClientState/JobGauge/Types/VPRGauge.cs +++ b/Dalamud/Game/ClientState/JobGauge/Types/VPRGauge.cs @@ -1,5 +1,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.Gauge; +using Reloaded.Memory; + using DreadCombo = Dalamud.Game.ClientState.JobGauge.Enums.DreadCombo; using SerpentCombo = Dalamud.Game.ClientState.JobGauge.Enums.SerpentCombo; @@ -22,25 +24,25 @@ public unsafe class VPRGauge : JobGaugeBase /// /// Gets how many uses of uncoiled fury the player has. /// - public byte RattlingCoilStacks => this.Struct->RattlingCoilStacks; + public byte RattlingCoilStacks => Struct->RattlingCoilStacks; /// /// Gets Serpent Offering stacks and gauge. /// - public byte SerpentOffering => this.Struct->SerpentOffering; + public byte SerpentOffering => Struct->SerpentOffering; /// /// Gets value indicating the use of 1st, 2nd, 3rd, 4th generation and Ouroboros. /// - public byte AnguineTribute => this.Struct->AnguineTribute; + public byte AnguineTribute => Struct->AnguineTribute; /// /// Gets the last Weaponskill used in DreadWinder/Pit of Dread combo. /// - public DreadCombo DreadCombo => (DreadCombo)this.Struct->DreadCombo; + public DreadCombo DreadCombo => (DreadCombo)Struct->DreadCombo; /// /// Gets current ability for Serpent's Tail. /// - public SerpentCombo SerpentCombo => (SerpentCombo)this.Struct->SerpentCombo; + public SerpentCombo SerpentCombo => (SerpentCombo)Struct->SerpentCombo; } diff --git a/Dalamud/Game/ClientState/Objects/ObjectTable.cs b/Dalamud/Game/ClientState/Objects/ObjectTable.cs index 9a2c7343e..b66dd4775 100644 --- a/Dalamud/Game/ClientState/Objects/ObjectTable.cs +++ b/Dalamud/Game/ClientState/Objects/ObjectTable.cs @@ -13,6 +13,8 @@ using Dalamud.Utility; using FFXIVClientStructs.Interop; +using Microsoft.Extensions.ObjectPool; + using CSGameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; using CSGameObjectManager = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObjectManager; @@ -35,6 +37,8 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable private readonly CachedEntry[] cachedObjectTable; + private readonly Enumerator?[] frameworkThreadEnumerators = new Enumerator?[4]; + [ServiceManager.ServiceConstructor] private unsafe ObjectTable() { @@ -44,6 +48,9 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable this.cachedObjectTable = new CachedEntry[objectTableLength]; for (var i = 0; i < this.cachedObjectTable.Length; i++) this.cachedObjectTable[i] = new(nativeObjectTable.GetPointer(i)); + + for (var i = 0; i < this.frameworkThreadEnumerators.Length; i++) + this.frameworkThreadEnumerators[i] = new(this, i); } /// @@ -236,25 +243,43 @@ internal sealed partial class ObjectTable public IEnumerator GetEnumerator() { ThreadSafety.AssertMainThread(); - return new Enumerator(this); + + // If we're on the framework thread, see if there's an already allocated enumerator available for use. + foreach (ref var x in this.frameworkThreadEnumerators.AsSpan()) + { + if (x is not null) + { + var t = x; + x = null; + t.Reset(); + return t; + } + } + + // No reusable enumerator is available; allocate a new temporary one. + return new Enumerator(this, -1); } /// IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); - private struct Enumerator(ObjectTable owner) : IEnumerator + private sealed class Enumerator(ObjectTable owner, int slotId) : IEnumerator, IResettable { + private ObjectTable? owner = owner; + private int index = -1; - public IGameObject Current { get; private set; } + public IGameObject Current { get; private set; } = null!; object IEnumerator.Current => this.Current; public bool MoveNext() { - var cache = owner.cachedObjectTable.AsSpan(); + if (this.index == objectTableLength) + return false; - while (++this.index < objectTableLength) + var cache = this.owner!.cachedObjectTable.AsSpan(); + for (this.index++; this.index < objectTableLength; this.index++) { if (cache[this.index].Update() is { } ao) { @@ -263,17 +288,24 @@ internal sealed partial class ObjectTable } } - this.Current = default; return false; } - public void Reset() - { - this.index = -1; - } + public void Reset() => this.index = -1; public void Dispose() { + if (this.owner is not { } o) + return; + + if (slotId != -1) + o.frameworkThreadEnumerators[slotId] = this; + } + + public bool TryReset() + { + this.Reset(); + return true; } } } diff --git a/Dalamud/Game/ClientState/Objects/TargetManager.cs b/Dalamud/Game/ClientState/Objects/TargetManager.cs index 5f317d077..f81154693 100644 --- a/Dalamud/Game/ClientState/Objects/TargetManager.cs +++ b/Dalamud/Game/ClientState/Objects/TargetManager.cs @@ -1,7 +1,6 @@ using Dalamud.Game.ClientState.Objects.Types; using Dalamud.IoC; using Dalamud.IoC.Internal; -using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game.Control; @@ -30,50 +29,50 @@ internal sealed unsafe class TargetManager : IServiceType, ITargetManager /// public IGameObject? Target { - get => this.objectTable.CreateObjectReference((IntPtr)this.Struct->GetHardTarget()); - set => this.Struct->SetHardTarget((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address); + get => this.objectTable.CreateObjectReference((IntPtr)Struct->GetHardTarget()); + set => Struct->SetHardTarget((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address); } /// public IGameObject? MouseOverTarget { - get => this.objectTable.CreateObjectReference((IntPtr)this.Struct->MouseOverTarget); - set => this.Struct->MouseOverTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; + get => this.objectTable.CreateObjectReference((IntPtr)Struct->MouseOverTarget); + set => Struct->MouseOverTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// public IGameObject? FocusTarget { - get => this.objectTable.CreateObjectReference((IntPtr)this.Struct->FocusTarget); - set => this.Struct->FocusTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; + get => this.objectTable.CreateObjectReference((IntPtr)Struct->FocusTarget); + set => Struct->FocusTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// public IGameObject? PreviousTarget { - get => this.objectTable.CreateObjectReference((IntPtr)this.Struct->PreviousTarget); - set => this.Struct->PreviousTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; + get => this.objectTable.CreateObjectReference((IntPtr)Struct->PreviousTarget); + set => Struct->PreviousTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// public IGameObject? SoftTarget { - get => this.objectTable.CreateObjectReference((IntPtr)this.Struct->GetSoftTarget()); - set => this.Struct->SetSoftTarget((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address); + get => this.objectTable.CreateObjectReference((IntPtr)Struct->GetSoftTarget()); + set => Struct->SetSoftTarget((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address); } /// public IGameObject? GPoseTarget { - get => this.objectTable.CreateObjectReference((IntPtr)this.Struct->GPoseTarget); - set => this.Struct->GPoseTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; + get => this.objectTable.CreateObjectReference((IntPtr)Struct->GPoseTarget); + set => Struct->GPoseTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// public IGameObject? MouseOverNameplateTarget { - get => this.objectTable.CreateObjectReference((IntPtr)this.Struct->MouseOverNameplateTarget); - set => this.Struct->MouseOverNameplateTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; + get => this.objectTable.CreateObjectReference((IntPtr)Struct->MouseOverNameplateTarget); + set => Struct->MouseOverNameplateTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } private TargetSystem* Struct => TargetSystem.Instance(); diff --git a/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs b/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs index 37f1f5504..238c81a72 100644 --- a/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs +++ b/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs @@ -1,4 +1,5 @@ using Dalamud.Game.ClientState.Statuses; +using Dalamud.Utility; namespace Dalamud.Game.ClientState.Objects.Types; diff --git a/Dalamud/Game/ClientState/Objects/Types/Character.cs b/Dalamud/Game/ClientState/Objects/Types/Character.cs index f122f1f27..a91ecc230 100644 --- a/Dalamud/Game/ClientState/Objects/Types/Character.cs +++ b/Dalamud/Game/ClientState/Objects/Types/Character.cs @@ -1,8 +1,9 @@ +using System.Runtime.CompilerServices; + using Dalamud.Data; -using Dalamud.Game.ClientState.Customize; using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Utility; +using Dalamud.Memory; using Lumina.Excel; using Lumina.Excel.Sheets; @@ -15,73 +16,68 @@ namespace Dalamud.Game.ClientState.Objects.Types; public interface ICharacter : IGameObject { /// - /// Gets the current HP of this character. + /// Gets the current HP of this Chara. /// public uint CurrentHp { get; } /// - /// Gets the maximum HP of this character. + /// Gets the maximum HP of this Chara. /// public uint MaxHp { get; } /// - /// Gets the current MP of this character. + /// Gets the current MP of this Chara. /// public uint CurrentMp { get; } /// - /// Gets the maximum MP of this character. + /// Gets the maximum MP of this Chara. /// public uint MaxMp { get; } /// - /// Gets the current GP of this character. + /// Gets the current GP of this Chara. /// public uint CurrentGp { get; } /// - /// Gets the maximum GP of this character. + /// Gets the maximum GP of this Chara. /// public uint MaxGp { get; } /// - /// Gets the current CP of this character. + /// Gets the current CP of this Chara. /// public uint CurrentCp { get; } /// - /// Gets the maximum CP of this character. + /// Gets the maximum CP of this Chara. /// public uint MaxCp { get; } /// - /// Gets the shield percentage of this character. + /// Gets the shield percentage of this Chara. /// public byte ShieldPercentage { get; } /// - /// Gets the ClassJob of this character. + /// Gets the ClassJob of this Chara. /// public RowRef ClassJob { get; } /// - /// Gets the level of this character. + /// Gets the level of this Chara. /// public byte Level { get; } /// - /// Gets a byte array describing the visual appearance of this character. + /// Gets a byte array describing the visual appearance of this Chara. /// Indexed by . /// public byte[] Customize { get; } /// - /// Gets the underlying CustomizeData struct for this character. - /// - public ICustomizeData CustomizeData { get; } - - /// - /// Gets the Free Company tag of this character. + /// Gets the Free Company tag of this chara. /// public SeString CompanyTag { get; } @@ -99,12 +95,12 @@ public interface ICharacter : IGameObject /// Gets the status flags. /// public StatusFlags StatusFlags { get; } - + /// /// Gets the current mount for this character. Will be null if the character doesn't have a mount. /// public RowRef? CurrentMount { get; } - + /// /// Gets the current minion summoned for this character. Will be null if the character doesn't have a minion. /// This method *will* return information about a spawned (but invisible) minion, e.g. if the character is riding a @@ -123,7 +119,7 @@ internal unsafe class Character : GameObject, ICharacter /// This represents a non-static entity. /// /// The address of this character in memory. - internal Character(nint address) + internal Character(IntPtr address) : base(address) { } @@ -162,12 +158,8 @@ internal unsafe class Character : GameObject, ICharacter public byte Level => this.Struct->CharacterData.Level; /// - [Api15ToDo("Do not allocate on each call, use the CS Span and let consumers do allocation if necessary")] public byte[] Customize => this.Struct->DrawData.CustomizeData.Data.ToArray(); - /// - public ICustomizeData CustomizeData => new CustomizeData((nint)(&this.Struct->DrawData.CustomizeData)); - /// public SeString CompanyTag => SeString.Parse(this.Struct->FreeCompanyTag); @@ -194,14 +186,14 @@ internal unsafe class Character : GameObject, ICharacter (this.Struct->IsAllianceMember ? StatusFlags.AllianceMember : StatusFlags.None) | (this.Struct->IsFriend ? StatusFlags.Friend : StatusFlags.None) | (this.Struct->IsCasting ? StatusFlags.IsCasting : StatusFlags.None); - + /// public RowRef? CurrentMount { get { if (this.Struct->IsNotMounted()) return null; // just for safety. - + var mountId = this.Struct->Mount.MountId; return mountId == 0 ? null : LuminaUtils.CreateRef(mountId); } @@ -212,7 +204,7 @@ internal unsafe class Character : GameObject, ICharacter { get { - if (this.Struct->CompanionObject != null) + if (this.Struct->CompanionObject != null) return LuminaUtils.CreateRef(this.Struct->CompanionObject->BaseId); // this is only present if a minion is summoned but hidden (e.g. the player's on a mount). diff --git a/Dalamud/Game/ClientState/Party/PartyList.cs b/Dalamud/Game/ClientState/Party/PartyList.cs index 0326350d2..9618b679c 100644 --- a/Dalamud/Game/ClientState/Party/PartyList.cs +++ b/Dalamud/Game/ClientState/Party/PartyList.cs @@ -9,7 +9,6 @@ using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; using CSGroupManager = FFXIVClientStructs.FFXIV.Client.Game.Group.GroupManager; -using CSPartyMember = FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember; namespace Dalamud.Game.ClientState.Party; @@ -44,20 +43,20 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList public bool IsAlliance => this.GroupManagerStruct->MainGroup.AllianceFlags > 0; /// - public unsafe nint GroupManagerAddress => (nint)CSGroupManager.Instance(); + public unsafe IntPtr GroupManagerAddress => (nint)CSGroupManager.Instance(); /// - public nint GroupListAddress => (nint)Unsafe.AsPointer(ref this.GroupManagerStruct->MainGroup.PartyMembers[0]); + public IntPtr GroupListAddress => (IntPtr)Unsafe.AsPointer(ref GroupManagerStruct->MainGroup.PartyMembers[0]); /// - public nint AllianceListAddress => (nint)Unsafe.AsPointer(ref this.GroupManagerStruct->MainGroup.AllianceMembers[0]); + public IntPtr AllianceListAddress => (IntPtr)Unsafe.AsPointer(ref this.GroupManagerStruct->MainGroup.AllianceMembers[0]); /// public long PartyId => this.GroupManagerStruct->MainGroup.PartyId; - private static int PartyMemberSize { get; } = Marshal.SizeOf(); + private static int PartyMemberSize { get; } = Marshal.SizeOf(); - private CSGroupManager* GroupManagerStruct => (CSGroupManager*)this.GroupManagerAddress; + private FFXIVClientStructs.FFXIV.Client.Game.Group.GroupManager* GroupManagerStruct => (FFXIVClientStructs.FFXIV.Client.Game.Group.GroupManager*)this.GroupManagerAddress; /// public IPartyMember? this[int index] @@ -82,45 +81,39 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList } /// - public nint GetPartyMemberAddress(int index) + public IntPtr GetPartyMemberAddress(int index) { if (index < 0 || index >= GroupLength) - return 0; + return IntPtr.Zero; return this.GroupListAddress + (index * PartyMemberSize); } /// - public IPartyMember? CreatePartyMemberReference(nint address) + public IPartyMember? CreatePartyMemberReference(IntPtr address) { - if (this.playerState.ContentId == 0) + if (address == IntPtr.Zero || !this.playerState.IsLoaded) return null; - if (address == 0) - return null; - - return new PartyMember((CSPartyMember*)address); + return new PartyMember(address); } /// - public nint GetAllianceMemberAddress(int index) + public IntPtr GetAllianceMemberAddress(int index) { if (index < 0 || index >= AllianceLength) - return 0; + return IntPtr.Zero; return this.AllianceListAddress + (index * PartyMemberSize); } /// - public IPartyMember? CreateAllianceMemberReference(nint address) + public IPartyMember? CreateAllianceMemberReference(IntPtr address) { - if (this.playerState.ContentId == 0) + if (address == IntPtr.Zero || !this.playerState.IsLoaded) return null; - if (address == 0) - return null; - - return new PartyMember((CSPartyMember*)address); + return new PartyMember(address); } } @@ -135,43 +128,18 @@ internal sealed partial class PartyList /// public IEnumerator GetEnumerator() { - return new Enumerator(this); + // Normally using Length results in a recursion crash, however we know the party size via ptr. + for (var i = 0; i < this.Length; i++) + { + var member = this[i]; + + if (member == null) + break; + + yield return member; + } } /// IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); - - private struct Enumerator(PartyList partyList) : IEnumerator - { - private int index = -1; - - public IPartyMember Current { get; private set; } - - object IEnumerator.Current => this.Current; - - public bool MoveNext() - { - while (++this.index < partyList.Length) - { - var partyMember = partyList[this.index]; - if (partyMember != null) - { - this.Current = partyMember; - return true; - } - } - - this.Current = default; - return false; - } - - public void Reset() - { - this.index = -1; - } - - public void Dispose() - { - } - } } diff --git a/Dalamud/Game/ClientState/Party/PartyMember.cs b/Dalamud/Game/ClientState/Party/PartyMember.cs index 843824318..4c738d866 100644 --- a/Dalamud/Game/ClientState/Party/PartyMember.cs +++ b/Dalamud/Game/ClientState/Party/PartyMember.cs @@ -1,29 +1,26 @@ -using System.Diagnostics.CodeAnalysis; using System.Numerics; +using System.Runtime.CompilerServices; using Dalamud.Data; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Statuses; using Dalamud.Game.Text.SeStringHandling; - -using Dalamud.Utility; +using Dalamud.Memory; using Lumina.Excel; -using CSPartyMember = FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember; - namespace Dalamud.Game.ClientState.Party; /// /// Interface representing a party member. /// -public interface IPartyMember : IEquatable +public interface IPartyMember { /// /// Gets the address of this party member in memory. /// - nint Address { get; } + IntPtr Address { get; } /// /// Gets a list of buffs or debuffs applied to this party member. @@ -111,82 +108,69 @@ public interface IPartyMember : IEquatable } /// -/// This struct represents a party member in the group manager. +/// This class represents a party member in the group manager. /// -/// A pointer to the PartyMember. -internal unsafe readonly struct PartyMember(CSPartyMember* ptr) : IPartyMember +internal unsafe class PartyMember : IPartyMember { - /// - public nint Address => (nint)ptr; + /// + /// Initializes a new instance of the class. + /// + /// Address of the party member. + internal PartyMember(IntPtr address) + { + this.Address = address; + } /// - public StatusList Statuses => new(&ptr->StatusManager); + public IntPtr Address { get; } /// - public Vector3 Position => ptr->Position; + public StatusList Statuses => new(&this.Struct->StatusManager); /// - [Api15ToDo("Change type to ulong.")] - public long ContentId => (long)ptr->ContentId; + public Vector3 Position => this.Struct->Position; /// - public uint ObjectId => ptr->EntityId; + public long ContentId => (long)this.Struct->ContentId; /// - public uint EntityId => ptr->EntityId; + public uint ObjectId => this.Struct->EntityId; + + /// + public uint EntityId => this.Struct->EntityId; /// public IGameObject? GameObject => Service.Get().SearchById(this.EntityId); /// - public uint CurrentHP => ptr->CurrentHP; + public uint CurrentHP => this.Struct->CurrentHP; /// - public uint MaxHP => ptr->MaxHP; + public uint MaxHP => this.Struct->MaxHP; /// - public ushort CurrentMP => ptr->CurrentMP; + public ushort CurrentMP => this.Struct->CurrentMP; /// - public ushort MaxMP => ptr->MaxMP; + public ushort MaxMP => this.Struct->MaxMP; /// - public RowRef Territory => LuminaUtils.CreateRef(ptr->TerritoryType); + public RowRef Territory => LuminaUtils.CreateRef(this.Struct->TerritoryType); /// - public RowRef World => LuminaUtils.CreateRef(ptr->HomeWorld); + public RowRef World => LuminaUtils.CreateRef(this.Struct->HomeWorld); /// - public SeString Name => SeString.Parse(ptr->Name); + public SeString Name => SeString.Parse(this.Struct->Name); /// - public byte Sex => ptr->Sex; + public byte Sex => this.Struct->Sex; /// - public RowRef ClassJob => LuminaUtils.CreateRef(ptr->ClassJob); + public RowRef ClassJob => LuminaUtils.CreateRef(this.Struct->ClassJob); /// - public byte Level => ptr->Level; + public byte Level => this.Struct->Level; - public static bool operator ==(PartyMember x, PartyMember y) => x.Equals(y); - - public static bool operator !=(PartyMember x, PartyMember y) => !(x == y); - - /// - public bool Equals(IPartyMember? other) - { - return this.EntityId == other.EntityId; - } - - /// - public override bool Equals([NotNullWhen(true)] object? obj) - { - return obj is PartyMember fate && this.Equals(fate); - } - - /// - public override int GetHashCode() - { - return this.EntityId.GetHashCode(); - } + private FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember*)this.Address; } diff --git a/Dalamud/Game/ClientState/Statuses/Status.cs b/Dalamud/Game/ClientState/Statuses/Status.cs index 160b15de5..2775f8f9b 100644 --- a/Dalamud/Game/ClientState/Statuses/Status.cs +++ b/Dalamud/Game/ClientState/Statuses/Status.cs @@ -1,49 +1,61 @@ -using System.Diagnostics.CodeAnalysis; - using Dalamud.Data; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.Types; using Lumina.Excel; -using CSStatus = FFXIVClientStructs.FFXIV.Client.Game.Status; - namespace Dalamud.Game.ClientState.Statuses; /// -/// Interface representing a status. +/// This class represents a status effect an actor is afflicted by. /// -public interface IStatus : IEquatable +public unsafe class Status { + /// + /// Initializes a new instance of the class. + /// + /// Status address. + internal Status(IntPtr address) + { + this.Address = address; + } + /// /// Gets the address of the status in memory. /// - nint Address { get; } + public IntPtr Address { get; } /// /// Gets the status ID of this status. /// - uint StatusId { get; } + public uint StatusId => this.Struct->StatusId; /// /// Gets the GameData associated with this status. /// - RowRef GameData { get; } + public RowRef GameData => LuminaUtils.CreateRef(this.Struct->StatusId); /// /// Gets the parameter value of the status. /// - ushort Param { get; } + public ushort Param => this.Struct->Param; + + /// + /// Gets the stack count of this status. + /// Only valid if this is a non-food status. + /// + [Obsolete($"Replaced with {nameof(Param)}", true)] + public byte StackCount => (byte)this.Struct->Param; /// /// Gets the time remaining of this status. /// - float RemainingTime { get; } + public float RemainingTime => this.Struct->RemainingTime; /// /// Gets the source ID of this status. /// - uint SourceId { get; } + public uint SourceId => this.Struct->SourceObject.ObjectId; /// /// Gets the source actor associated with this status. @@ -51,55 +63,7 @@ public interface IStatus : IEquatable /// /// This iterates the actor table, it should be used with care. /// - IGameObject? SourceObject { get; } -} - -/// -/// This struct represents a status effect an actor is afflicted by. -/// -/// A pointer to the Status. -internal unsafe readonly struct Status(CSStatus* ptr) : IStatus -{ - /// - public nint Address => (nint)ptr; - - /// - public uint StatusId => ptr->StatusId; - - /// - public RowRef GameData => LuminaUtils.CreateRef(ptr->StatusId); - - /// - public ushort Param => ptr->Param; - - /// - public float RemainingTime => ptr->RemainingTime; - - /// - public uint SourceId => ptr->SourceObject.ObjectId; - - /// public IGameObject? SourceObject => Service.Get().SearchById(this.SourceId); - public static bool operator ==(Status x, Status y) => x.Equals(y); - - public static bool operator !=(Status x, Status y) => !(x == y); - - /// - public bool Equals(IStatus? other) - { - return this.StatusId == other.StatusId && this.SourceId == other.SourceId && this.Param == other.Param && this.RemainingTime == other.RemainingTime; - } - - /// - public override bool Equals([NotNullWhen(true)] object? obj) - { - return obj is Status fate && this.Equals(fate); - } - - /// - public override int GetHashCode() - { - return HashCode.Combine(this.StatusId, this.SourceId, this.Param, this.RemainingTime); - } + private FFXIVClientStructs.FFXIV.Client.Game.Status* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Status*)this.Address; } diff --git a/Dalamud/Game/ClientState/Statuses/StatusList.cs b/Dalamud/Game/ClientState/Statuses/StatusList.cs index 2e8024ab8..410ae9d7c 100644 --- a/Dalamud/Game/ClientState/Statuses/StatusList.cs +++ b/Dalamud/Game/ClientState/Statuses/StatusList.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using CSStatus = FFXIVClientStructs.FFXIV.Client.Game.Status; +using Dalamud.Game.Player; namespace Dalamud.Game.ClientState.Statuses; @@ -16,7 +16,7 @@ public sealed unsafe partial class StatusList /// Initializes a new instance of the class. /// /// Address of the status list. - internal StatusList(nint address) + internal StatusList(IntPtr address) { this.Address = address; } @@ -26,19 +26,19 @@ public sealed unsafe partial class StatusList /// /// Pointer to the status list. internal unsafe StatusList(void* pointer) - : this((nint)pointer) + : this((IntPtr)pointer) { } /// /// Gets the address of the status list in memory. /// - public nint Address { get; } + public IntPtr Address { get; } /// /// Gets the amount of status effect slots the actor has. /// - public int Length => this.Struct->NumValidStatuses; + public int Length => Struct->NumValidStatuses; private static int StatusSize { get; } = Marshal.SizeOf(); @@ -49,7 +49,7 @@ public sealed unsafe partial class StatusList /// /// Status Index. /// The status at the specified index. - public IStatus? this[int index] + public Status? this[int index] { get { @@ -66,7 +66,7 @@ public sealed unsafe partial class StatusList /// /// The address of the status list in memory. /// The status object containing the requested data. - public static StatusList? CreateStatusListReference(nint address) + public static StatusList? CreateStatusListReference(IntPtr address) { if (address == IntPtr.Zero) return null; @@ -74,12 +74,8 @@ public sealed unsafe partial class StatusList // The use case for CreateStatusListReference and CreateStatusReference to be static is so // fake status lists can be generated. Since they aren't exposed as services, it's either // here or somewhere else. - var clientState = Service.Get(); - - if (clientState.LocalContentId == 0) - return null; - - if (address == 0) + var playerState = Service.Get(); + if (!playerState.IsLoaded) return null; return new StatusList(address); @@ -90,15 +86,16 @@ public sealed unsafe partial class StatusList ///
/// The address of the status effect in memory. /// The status object containing the requested data. - public static IStatus? CreateStatusReference(nint address) + public static Status? CreateStatusReference(IntPtr address) { if (address == IntPtr.Zero) return null; - if (address == 0) + var playerState = Service.Get(); + if (!playerState.IsLoaded) return null; - return new Status((CSStatus*)address); + return new Status(address); } /// @@ -106,22 +103,22 @@ public sealed unsafe partial class StatusList /// /// The index of the status. /// The memory address of the status. - public nint GetStatusAddress(int index) + public IntPtr GetStatusAddress(int index) { if (index < 0 || index >= this.Length) - return 0; + return IntPtr.Zero; - return (nint)Unsafe.AsPointer(ref this.Struct->Status[index]); + return (IntPtr)Unsafe.AsPointer(ref this.Struct->Status[index]); } } /// /// This collection represents the status effects an actor is afflicted by. /// -public sealed partial class StatusList : IReadOnlyCollection, ICollection +public sealed partial class StatusList : IReadOnlyCollection, ICollection { /// - int IReadOnlyCollection.Count => this.Length; + int IReadOnlyCollection.Count => this.Length; /// int ICollection.Count => this.Length; @@ -133,9 +130,17 @@ public sealed partial class StatusList : IReadOnlyCollection, ICollecti object ICollection.SyncRoot => this; /// - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { - return new Enumerator(this); + for (var i = 0; i < this.Length; i++) + { + var status = this[i]; + + if (status == null || status.StatusId == 0) + continue; + + yield return status; + } } /// @@ -150,38 +155,4 @@ public sealed partial class StatusList : IReadOnlyCollection, ICollecti index++; } } - - private struct Enumerator(StatusList statusList) : IEnumerator - { - private int index = -1; - - public IStatus Current { get; private set; } - - object IEnumerator.Current => this.Current; - - public bool MoveNext() - { - while (++this.index < statusList.Length) - { - var status = statusList[this.index]; - if (status != null && status.StatusId != 0) - { - this.Current = status; - return true; - } - } - - this.Current = default; - return false; - } - - public void Reset() - { - this.index = -1; - } - - public void Dispose() - { - } - } } diff --git a/Dalamud/Game/ClientState/Structs/StatusEffect.cs b/Dalamud/Game/ClientState/Structs/StatusEffect.cs new file mode 100644 index 000000000..2a60a7d3b --- /dev/null +++ b/Dalamud/Game/ClientState/Structs/StatusEffect.cs @@ -0,0 +1,35 @@ +using System.Runtime.InteropServices; + +namespace Dalamud.Game.ClientState.Structs; + +/// +/// Native memory representation of a FFXIV status effect. +/// +[StructLayout(LayoutKind.Sequential)] +public struct StatusEffect +{ + /// + /// The effect ID. + /// + public short EffectId; + + /// + /// How many stacks are present. + /// + public byte StackCount; + + /// + /// Additional parameters. + /// + public byte Param; + + /// + /// The duration remaining. + /// + public float Duration; + + /// + /// The ID of the actor that caused this effect. + /// + public int OwnerId; +} diff --git a/Dalamud/Game/ClientState/ZoneInit.cs b/Dalamud/Game/ClientState/ZoneInit.cs index 7eb4576aa..5c2213c90 100644 --- a/Dalamud/Game/ClientState/ZoneInit.cs +++ b/Dalamud/Game/ClientState/ZoneInit.cs @@ -59,7 +59,7 @@ public class ZoneInitEventArgs : EventArgs eventArgs.ContentFinderCondition = dataManager.GetExcelSheet().GetRow(*(ushort*)(packet + 0x06)); eventArgs.Weather = dataManager.GetExcelSheet().GetRow(*(byte*)(packet + 0x10)); - const int NumFestivals = 8; + const int NumFestivals = 4; eventArgs.ActiveFestivals = new Festival[NumFestivals]; eventArgs.ActiveFestivalPhases = new ushort[NumFestivals]; @@ -67,7 +67,7 @@ public class ZoneInitEventArgs : EventArgs // but it's unclear why they exist as separate entries and why they would be different. for (var i = 0; i < NumFestivals; i++) { - eventArgs.ActiveFestivals[i] = dataManager.GetExcelSheet().GetRow(*(ushort*)(packet + 0x26 + (i * 2))); + eventArgs.ActiveFestivals[i] = dataManager.GetExcelSheet().GetRow(*(ushort*)(packet + 0x2E + (i * 2))); eventArgs.ActiveFestivalPhases[i] = *(ushort*)(packet + 0x36 + (i * 2)); } diff --git a/Dalamud/Game/Command/CommandManager.cs b/Dalamud/Game/Command/CommandManager.cs index e9abb7336..01442c409 100644 --- a/Dalamud/Game/Command/CommandManager.cs +++ b/Dalamud/Game/Command/CommandManager.cs @@ -24,7 +24,7 @@ namespace Dalamud.Game.Command; [ServiceManager.EarlyLoadedService] internal sealed unsafe class CommandManager : IInternalDisposableService, ICommandManager { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("Command"); private readonly ConcurrentDictionary commandMap = new(); private readonly ConcurrentDictionary<(string, IReadOnlyCommandInfo), string> commandAssemblyNameMap = new(); @@ -71,7 +71,7 @@ internal sealed unsafe class CommandManager : IInternalDisposableService, IComma if (separatorPosition + 1 >= content.Length) { // Remove the trailing space - command = content[..separatorPosition]; + command = content.Substring(0, separatorPosition); } else { @@ -262,12 +262,12 @@ internal sealed unsafe class CommandManager : IInternalDisposableService, IComma #pragma warning restore SA1015 internal class CommandManagerPluginScoped : IInternalDisposableService, ICommandManager { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("Command"); [ServiceManager.ServiceDependency] private readonly CommandManager commandManagerService = Service.Get(); - private readonly List pluginRegisteredCommands = []; + private readonly List pluginRegisteredCommands = new(); private readonly LocalPlugin pluginInfo; /// diff --git a/Dalamud/Game/Config/GameConfig.cs b/Dalamud/Game/Config/GameConfig.cs index a55056351..9579d84bc 100644 --- a/Dalamud/Game/Config/GameConfig.cs +++ b/Dalamud/Game/Config/GameConfig.cs @@ -1,13 +1,11 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; using Dalamud.Utility; - using FFXIVClientStructs.FFXIV.Common.Configuration; - using Serilog; namespace Dalamud.Game.Config; diff --git a/Dalamud/Game/Config/GameConfigAddressResolver.cs b/Dalamud/Game/Config/GameConfigAddressResolver.cs index e03f4f40b..2491c4033 100644 --- a/Dalamud/Game/Config/GameConfigAddressResolver.cs +++ b/Dalamud/Game/Config/GameConfigAddressResolver.cs @@ -1,6 +1,4 @@ -using Dalamud.Plugin.Services; - -namespace Dalamud.Game.Config; +namespace Dalamud.Game.Config; /// /// Game config system address resolver. diff --git a/Dalamud/Game/Config/GameConfigSection.cs b/Dalamud/Game/Config/GameConfigSection.cs index eb2f1107e..8ebab8a60 100644 --- a/Dalamud/Game/Config/GameConfigSection.cs +++ b/Dalamud/Game/Config/GameConfigSection.cs @@ -1,12 +1,10 @@ -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Diagnostics; using System.Text; using Dalamud.Memory; using Dalamud.Utility; - using FFXIVClientStructs.FFXIV.Common.Configuration; - using Serilog; namespace Dalamud.Game.Config; diff --git a/Dalamud/Game/Config/UiConfigOption.cs b/Dalamud/Game/Config/UiConfigOption.cs index 0cfc3b1e9..f6a9aaa21 100644 --- a/Dalamud/Game/Config/UiConfigOption.cs +++ b/Dalamud/Game/Config/UiConfigOption.cs @@ -4069,13 +4069,6 @@ public enum UiConfigOption [GameConfigOption("GposePortraitRotateType", ConfigType.UInt)] GposePortraitRotateType, - /// - /// UiConfig option with the internal name GroupPosePortraitUnlockAspectLimit. - /// This option is a UInt. - /// - [GameConfigOption("GroupPosePortraitUnlockAspectLimit", ConfigType.UInt)] - GroupPosePortraitUnlockAspectLimit, - /// /// UiConfig option with the internal name LsListSortPriority. /// This option is a UInt. diff --git a/Dalamud/Game/DutyState/DutyStateAddressResolver.cs b/Dalamud/Game/DutyState/DutyStateAddressResolver.cs index 480b699a0..1bca93efb 100644 --- a/Dalamud/Game/DutyState/DutyStateAddressResolver.cs +++ b/Dalamud/Game/DutyState/DutyStateAddressResolver.cs @@ -1,5 +1,3 @@ -using Dalamud.Plugin.Services; - namespace Dalamud.Game.DutyState; /// diff --git a/Dalamud/Game/Framework.cs b/Dalamud/Game/Framework.cs index e40274043..808bbce50 100644 --- a/Dalamud/Game/Framework.cs +++ b/Dalamud/Game/Framework.cs @@ -26,7 +26,7 @@ namespace Dalamud.Game; [ServiceManager.EarlyLoadedService] internal sealed class Framework : IInternalDisposableService, IFramework { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("Framework"); private static readonly Stopwatch StatsStopwatch = new(); @@ -86,7 +86,7 @@ internal sealed class Framework : IInternalDisposableService, IFramework /// /// Gets the stats history mapping. /// - public static Dictionary> StatsHistory { get; } = []; + public static Dictionary> StatsHistory { get; } = new(); /// public DateTime LastUpdate { get; private set; } = DateTime.MinValue; @@ -106,7 +106,7 @@ internal sealed class Framework : IInternalDisposableService, IFramework /// /// Gets the list of update sub-delegates that didn't get updated this frame. /// - internal List NonUpdatedSubDelegates { get; private set; } = []; + internal List NonUpdatedSubDelegates { get; private set; } = new(); /// /// Gets or sets a value indicating whether to dispatch update events. @@ -121,9 +121,9 @@ internal sealed class Framework : IInternalDisposableService, IFramework /// public Task DelayTicks(long numTicks, CancellationToken cancellationToken = default) { - if (this.frameworkDestroy.IsCancellationRequested) // Going away + if (this.frameworkDestroy.IsCancellationRequested) return Task.FromCanceled(this.frameworkDestroy.Token); - if (numTicks <= 0 || this.frameworkThreadTaskScheduler.BoundThread == null) // Nonsense or before first tick + if (numTicks <= 0) return Task.CompletedTask; var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -212,10 +212,11 @@ internal sealed class Framework : IInternalDisposableService, IFramework if (cancellationToken == default) cancellationToken = this.FrameworkThreadTaskFactory.CancellationToken; return this.FrameworkThreadTaskFactory.ContinueWhenAll( - [ + new[] + { Task.Delay(delay, cancellationToken), this.DelayTicks(delayTicks, cancellationToken), - ], + }, _ => func(), cancellationToken, TaskContinuationOptions.HideScheduler, @@ -238,10 +239,11 @@ internal sealed class Framework : IInternalDisposableService, IFramework if (cancellationToken == default) cancellationToken = this.FrameworkThreadTaskFactory.CancellationToken; return this.FrameworkThreadTaskFactory.ContinueWhenAll( - [ + new[] + { Task.Delay(delay, cancellationToken), this.DelayTicks(delayTicks, cancellationToken), - ], + }, _ => action(), cancellationToken, TaskContinuationOptions.HideScheduler, @@ -264,10 +266,11 @@ internal sealed class Framework : IInternalDisposableService, IFramework if (cancellationToken == default) cancellationToken = this.FrameworkThreadTaskFactory.CancellationToken; return this.FrameworkThreadTaskFactory.ContinueWhenAll( - [ + new[] + { Task.Delay(delay, cancellationToken), this.DelayTicks(delayTicks, cancellationToken), - ], + }, _ => func(), cancellationToken, TaskContinuationOptions.HideScheduler, @@ -290,10 +293,11 @@ internal sealed class Framework : IInternalDisposableService, IFramework if (cancellationToken == default) cancellationToken = this.FrameworkThreadTaskFactory.CancellationToken; return this.FrameworkThreadTaskFactory.ContinueWhenAll( - [ + new[] + { Task.Delay(delay, cancellationToken), this.DelayTicks(delayTicks, cancellationToken), - ], + }, _ => func(), cancellationToken, TaskContinuationOptions.HideScheduler, @@ -329,7 +333,7 @@ internal sealed class Framework : IInternalDisposableService, IFramework internal static void AddToStats(string key, double ms) { if (!StatsHistory.ContainsKey(key)) - StatsHistory.Add(key, []); + StatsHistory.Add(key, new List()); StatsHistory[key].Add(ms); diff --git a/Dalamud/Game/Gui/ChatGui.cs b/Dalamud/Game/Gui/ChatGui.cs index c514752da..d7303c4ce 100644 --- a/Dalamud/Game/Gui/ChatGui.cs +++ b/Dalamud/Game/Gui/ChatGui.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Dalamud.Configuration.Internal; @@ -27,6 +26,7 @@ using Lumina.Text; using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; +using LSeStringBuilder = Lumina.Text.SeStringBuilder; using SeString = Dalamud.Game.Text.SeStringHandling.SeString; using SeStringBuilder = Dalamud.Game.Text.SeStringHandling.SeStringBuilder; @@ -38,16 +38,14 @@ namespace Dalamud.Game.Gui; [ServiceManager.EarlyLoadedService] internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("ChatGui"); private readonly Queue chatQueue = new(); - private readonly Dictionary<(string PluginName, uint CommandId), Action> dalamudLinkHandlers = []; - private readonly List seenLogMessageObjects = []; + private readonly Dictionary<(string PluginName, uint CommandId), Action> dalamudLinkHandlers = new(); private readonly Hook printMessageHook; private readonly Hook inventoryItemCopyHook; private readonly Hook handleLinkClickHook; - private readonly Hook handleLogModuleUpdate; [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); @@ -61,12 +59,10 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui this.printMessageHook = Hook.FromAddress(RaptureLogModule.Addresses.PrintMessage.Value, this.HandlePrintMessageDetour); this.inventoryItemCopyHook = Hook.FromAddress((nint)InventoryItem.StaticVirtualTablePointer->Copy, this.InventoryItemCopyDetour); this.handleLinkClickHook = Hook.FromAddress(LogViewer.Addresses.HandleLinkClick.Value, this.HandleLinkClickDetour); - this.handleLogModuleUpdate = Hook.FromAddress(RaptureLogModule.Addresses.Update.Value, this.UpdateDetour); this.printMessageHook.Enable(); this.inventoryItemCopyHook.Enable(); this.handleLinkClickHook.Enable(); - this.handleLogModuleUpdate.Enable(); } [UnmanagedFunctionPointer(CallingConvention.ThisCall)] @@ -84,9 +80,6 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui /// public event IChatGui.OnMessageUnhandledDelegate? ChatMessageUnhandled; - /// - public event IChatGui.OnLogMessageDelegate? LogMessage; - /// public uint LastLinkedItemId { get; private set; } @@ -118,7 +111,6 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui this.printMessageHook.Dispose(); this.inventoryItemCopyHook.Dispose(); this.handleLinkClickHook.Dispose(); - this.handleLogModuleUpdate.Dispose(); } #region DalamudSeString @@ -215,21 +207,21 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui if (this.chatQueue.Count == 0) return; - using var rssb = new RentedSeStringBuilder(); + var sb = LSeStringBuilder.SharedPool.Get(); Span namebuf = stackalloc byte[256]; using var sender = new Utf8String(); using var message = new Utf8String(); while (this.chatQueue.TryDequeue(out var chat)) { - rssb.Builder.Clear(); + sb.Clear(); foreach (var c in UtfEnumerator.From(chat.MessageBytes, UtfEnumeratorFlags.Utf8SeString)) { if (c.IsSeStringPayload) - rssb.Builder.Append((ReadOnlySeStringSpan)chat.MessageBytes.AsSpan(c.ByteOffset, c.ByteLength)); + sb.Append((ReadOnlySeStringSpan)chat.MessageBytes.AsSpan(c.ByteOffset, c.ByteLength)); else if (c.Value.IntValue == 0x202F) - rssb.Builder.BeginMacro(MacroCode.NonBreakingSpace).EndMacro(); + sb.BeginMacro(MacroCode.NonBreakingSpace).EndMacro(); else - rssb.Builder.Append(c); + sb.Append(c); } if (chat.NameBytes.Length + 1 < namebuf.Length) @@ -243,7 +235,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui sender.SetString(chat.NameBytes.NullTerminate()); } - message.SetString(rssb.Builder.GetViewAsSpan()); + message.SetString(sb.GetViewAsSpan()); var targetChannel = chat.Type ?? this.configuration.GeneralChatType; @@ -255,6 +247,8 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui chat.Timestamp, (byte)(chat.Silent ? 1 : 0)); } + + LSeStringBuilder.SharedPool.Return(sb); } /// @@ -332,28 +326,29 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui private void PrintTagged(ReadOnlySpan message, XivChatType channel, string? tag, ushort? color) { - using var rssb = new RentedSeStringBuilder(); + var sb = LSeStringBuilder.SharedPool.Get(); if (!tag.IsNullOrEmpty()) { if (color is not null) { - rssb.Builder - .PushColorType(color.Value) - .Append($"[{tag}] ") - .PopColorType(); + sb.PushColorType(color.Value); + sb.Append($"[{tag}] "); + sb.PopColorType(); } else { - rssb.Builder.Append($"[{tag}] "); + sb.Append($"[{tag}] "); } } this.Print(new XivChatEntry { - MessageBytes = rssb.Builder.Append((ReadOnlySeStringSpan)message).ToArray(), + MessageBytes = sb.Append((ReadOnlySeStringSpan)message).ToArray(), Type = channel, }); + + LSeStringBuilder.SharedPool.Return(sb); } private void InventoryItemCopyDetour(InventoryItem* thisPtr, InventoryItem* otherPtr) @@ -462,8 +457,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui Log.Verbose($"InteractableLinkClicked: {Payload.EmbeddedInfoType.DalamudLink}"); - using var rssb = new RentedSeStringBuilder(); - + var sb = LSeStringBuilder.SharedPool.Get(); try { var seStringSpan = new ReadOnlySeStringSpan(linkData->Payload); @@ -471,7 +465,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui // read until link terminator foreach (var payload in seStringSpan) { - rssb.Builder.Append(payload); + sb.Append(payload); if (payload.Type == ReadOnlySePayloadType.Macro && payload.MacroCode == MacroCode.Link && @@ -483,7 +477,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui } } - var seStr = SeString.Parse(rssb.Builder.ToArray()); + var seStr = SeString.Parse(sb.ToArray()); if (seStr.Payloads.Count == 0 || seStr.Payloads[0] is not DalamudLinkPayload link) return; @@ -501,45 +495,9 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui { Log.Error(ex, "Exception in HandleLinkClickDetour"); } - } - - private void UpdateDetour(RaptureLogModule* thisPtr) - { - try + finally { - foreach (ref var item in thisPtr->LogMessageQueue) - { - var logMessage = new Chat.LogMessage((LogMessageQueueItem*)Unsafe.AsPointer(ref item)); - - // skip any entries that survived the previous Update call as the event was already called for them - if (this.seenLogMessageObjects.Contains(logMessage.Address)) - continue; - - foreach (var action in Delegate.EnumerateInvocationList(this.LogMessage)) - { - try - { - action(logMessage); - } - catch (Exception e) - { - Log.Error(e, "Could not invoke registered OnLogMessageDelegate for {Name}", action.Method); - } - } - } - - this.handleLogModuleUpdate.Original(thisPtr); - - // record the log messages for that we already called the event, but are still in the queue - this.seenLogMessageObjects.Clear(); - foreach (ref var item in thisPtr->LogMessageQueue) - { - this.seenLogMessageObjects.Add((nint)Unsafe.AsPointer(ref item)); - } - } - catch (Exception ex) - { - Log.Error(ex, "Exception in UpdateDetour"); + LSeStringBuilder.SharedPool.Return(sb); } } } @@ -570,7 +528,6 @@ internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui this.chatGuiService.CheckMessageHandled += this.OnCheckMessageForward; this.chatGuiService.ChatMessageHandled += this.OnMessageHandledForward; this.chatGuiService.ChatMessageUnhandled += this.OnMessageUnhandledForward; - this.chatGuiService.LogMessage += this.OnLogMessageForward; } /// @@ -585,9 +542,6 @@ internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui /// public event IChatGui.OnMessageUnhandledDelegate? ChatMessageUnhandled; - /// - public event IChatGui.OnLogMessageDelegate? LogMessage; - /// public uint LastLinkedItemId => this.chatGuiService.LastLinkedItemId; @@ -604,13 +558,11 @@ internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui this.chatGuiService.CheckMessageHandled -= this.OnCheckMessageForward; this.chatGuiService.ChatMessageHandled -= this.OnMessageHandledForward; this.chatGuiService.ChatMessageUnhandled -= this.OnMessageUnhandledForward; - this.chatGuiService.LogMessage -= this.OnLogMessageForward; this.ChatMessage = null; this.CheckMessageHandled = null; this.ChatMessageHandled = null; this.ChatMessageUnhandled = null; - this.LogMessage = null; } /// @@ -664,7 +616,4 @@ internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui private void OnMessageUnhandledForward(XivChatType type, int timestamp, SeString sender, SeString message) => this.ChatMessageUnhandled?.Invoke(type, timestamp, sender, message); - - private void OnLogMessageForward(Chat.ILogMessage message) - => this.LogMessage?.Invoke(message); } diff --git a/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs b/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs index ab3da7bf3..7512f4160 100644 --- a/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs +++ b/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Threading; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; @@ -29,10 +28,10 @@ namespace Dalamud.Game.Gui.ContextMenu; [ServiceManager.EarlyLoadedService] internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextMenu { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("ContextMenu"); private readonly Hook atkModuleVf22OpenAddonByAgentHook; - private readonly Hook addonContextMenuOnMenuSelectedHook; + private readonly Hook addonContextMenuOnMenuSelectedHook; private uint? addonContextSubNameId; @@ -41,7 +40,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM { var raptureAtkModuleVtable = (nint*)RaptureAtkModule.StaticVirtualTablePointer; this.atkModuleVf22OpenAddonByAgentHook = Hook.FromAddress(raptureAtkModuleVtable[22], this.AtkModuleVf22OpenAddonByAgentDetour); - this.addonContextMenuOnMenuSelectedHook = Hook.FromAddress((nint)AddonContextMenu.StaticVirtualTablePointer->OnMenuSelected, this.AddonContextMenuOnMenuSelectedDetour); + this.addonContextMenuOnMenuSelectedHook = Hook.FromAddress((nint)AddonContextMenu.StaticVirtualTablePointer->OnMenuSelected, this.AddonContextMenuOnMenuSelectedDetour); this.atkModuleVf22OpenAddonByAgentHook.Enable(); this.addonContextMenuOnMenuSelectedHook.Enable(); @@ -49,12 +48,16 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM private delegate ushort AtkModuleVf22OpenAddonByAgentDelegate(AtkModule* module, byte* addonName, int valueCount, AtkValue* values, AgentInterface* agent, nint a7, bool a8); + private delegate bool AddonContextMenuOnMenuSelectedDelegate(AddonContextMenu* addon, int selectedIdx, byte a3); + + private delegate ushort RaptureAtkModuleOpenAddonDelegate(RaptureAtkModule* a1, uint addonNameId, uint valueCount, AtkValue* values, AgentInterface* parentAgent, ulong unk, ushort parentAddonId, int unk2); + /// public event IContextMenu.OnMenuOpenedDelegate? OnMenuOpened; private Dictionary> MenuItems { get; } = []; - private Lock MenuItemsLock { get; } = new(); + private object MenuItemsLock { get; } = new(); private AgentInterface* SelectedAgent { get; set; } @@ -182,7 +185,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM values[0].ChangeType(ValueType.UInt); values[0].UInt = 0; values[1].ChangeType(ValueType.String); - values[1].SetManagedString(name.EncodeWithNullTerminator()); + values[1].SetManagedString(name.Encode().NullTerminate()); values[2].ChangeType(ValueType.Int); values[2].Int = x; values[3].ChangeType(ValueType.Int); @@ -262,7 +265,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM submenuMask |= 1u << i; nameData[i].ChangeType(ValueType.String); - nameData[i].SetManagedString(this.GetPrefixedName(item).EncodeWithNullTerminator()); + nameData[i].SetManagedString(this.GetPrefixedName(item).Encode().NullTerminate()); } for (var i = 0; i < prefixMenuSize; ++i) @@ -292,9 +295,8 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM // 2: UInt = Return Mask (?) // 3: UInt = Submenu Mask // 4: UInt = OpenAtCursorPosition ? 2 : 1 - // 5: UInt = ? - // 6: UInt = ? - // 7: UInt = ? + // 5: UInt = 0 + // 6: UInt = 0 foreach (var item in items) { @@ -310,7 +312,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM } } - this.SetupGenericMenu(8, 0, 2, 3, items, ref valueCount, ref values); + this.SetupGenericMenu(7, 0, 2, 3, items, ref valueCount, ref values); } private void SetupContextSubMenu(IReadOnlyList items, ref int valueCount, ref AtkValue* values) @@ -336,7 +338,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM this.MenuCallbackIds.Clear(); this.SelectedAgent = agent; var unitManager = RaptureAtkUnitManager.Instance(); - this.SelectedParentAddon = unitManager->GetAddonById(unitManager->GetAddonByName(addonName)->BlockedParentId); + this.SelectedParentAddon = unitManager->GetAddonById(unitManager->GetAddonByName(addonName)->ContextMenuParentId); this.SelectedEventInterfaces.Clear(); if (this.SelectedAgent == AgentInventoryContext.Instance()) { diff --git a/Dalamud/Game/Gui/ContextMenu/MenuArgs.cs b/Dalamud/Game/Gui/ContextMenu/MenuArgs.cs index 900935ed5..39fd1c52c 100644 --- a/Dalamud/Game/Gui/ContextMenu/MenuArgs.cs +++ b/Dalamud/Game/Gui/ContextMenu/MenuArgs.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; +using Dalamud.Memory; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.UI.Agent; diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs index e5de6b2bd..2235c5ade 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBar.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs @@ -30,7 +30,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar { private const uint BaseNodeId = 1000; - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("DtrBar"); [ServiceManager.ServiceDependency] private readonly Framework framework = Service.Get(); @@ -54,7 +54,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar private readonly ReaderWriterLockSlim entriesLock = new(); private readonly List entries = []; - private readonly Dictionary> eventHandles = []; + private readonly Dictionary> eventHandles = new(); private ImmutableList? entriesReadOnlyCopy; @@ -397,15 +397,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar ushort w = 0, h = 0; node->GetTextDrawSize(&w, &h, node->NodeText.StringPtr); - - if (data.MinimumWidth > 0) - { - node->SetWidth(Math.Max(data.MinimumWidth, w)); - } - else - { - node->SetWidth(w); - } + node->SetWidth(w); } var elementWidth = data.TextNode->Width + this.configuration.DtrSpacing; @@ -524,7 +516,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar var node = data.TextNode = this.MakeNode(++this.runningNodeIds); - this.eventHandles.TryAdd(node->NodeId, []); + this.eventHandles.TryAdd(node->NodeId, new List()); this.eventHandles[node->NodeId].AddRange(new List { this.uiEventManager.AddEvent(AddonEventManager.DalamudInternalKey, (nint)dtr, (nint)node, AddonEventType.MouseOver, this.DtrEventHandler), diff --git a/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs b/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs index 47e86fde1..f5b7011fe 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs @@ -1,6 +1,7 @@ -using System.Numerics; +using System.Numerics; using Dalamud.Configuration.Internal; +using Dalamud.Game.Addon.Events.EventDataTypes; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Plugin.Internal.Types; using Dalamud.Utility; @@ -40,11 +41,6 @@ public interface IReadOnlyDtrBarEntry /// public bool Shown { get; } - /// - /// Gets a value indicating this entry's minimum width. - /// - public ushort MinimumWidth { get; } - /// /// Gets a value indicating whether the user has hidden this entry from view through the Dalamud settings. /// @@ -81,11 +77,6 @@ public interface IDtrBarEntry : IReadOnlyDtrBarEntry /// public new bool Shown { get; set; } - /// - /// Gets or sets a value specifying the requested minimum width to make this entry. - /// - public new ushort MinimumWidth { get; set; } - /// /// Gets or sets an action to be invoked when the user clicks on the dtr entry. /// @@ -138,25 +129,6 @@ internal sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry /// public SeString? Tooltip { get; set; } - /// - public ushort MinimumWidth - { - get; - set - { - field = value; - if (this.TextNode is not null) - { - if (this.TextNode->GetWidth() < value) - { - this.TextNode->SetWidth(value); - } - } - - this.Dirty = true; - } - } - /// public Action? OnClick { get; set; } @@ -178,7 +150,7 @@ internal sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry } /// - [Api15ToDo("Maybe make this config scoped to internal name?")] + [Api13ToDo("Maybe make this config scoped to internal name?")] public bool UserHidden => this.configuration.DtrIgnore?.Contains(this.Title) ?? false; /// diff --git a/Dalamud/Game/Gui/FlyText/FlyTextKind.cs b/Dalamud/Game/Gui/FlyText/FlyTextKind.cs index da448c683..2b8325927 100644 --- a/Dalamud/Game/Gui/FlyText/FlyTextKind.cs +++ b/Dalamud/Game/Gui/FlyText/FlyTextKind.cs @@ -92,16 +92,34 @@ public enum FlyTextKind : int /// IslandExp = 15, + /// + /// Val1 in serif font next to all caps condensed font Text1 with Text2 in sans-serif as subtitle. + /// + [Obsolete("Use Dataset instead", true)] + Unknown16 = 16, + /// /// Val1 in serif font next to all caps condensed font Text1 with Text2 in sans-serif as subtitle. /// Dataset = 16, + /// + /// Val1 in serif font, Text2 in sans-serif as subtitle. + /// + [Obsolete("Use Knowledge instead", true)] + Unknown17 = 17, + /// /// Val1 in serif font, Text2 in sans-serif as subtitle. /// Knowledge = 17, + /// + /// Val1 in serif font, Text2 in sans-serif as subtitle. + /// + [Obsolete("Use PhantomExp instead", true)] + Unknown18 = 18, + /// /// Val1 in serif font, Text2 in sans-serif as subtitle. /// diff --git a/Dalamud/Game/Gui/GameGui.cs b/Dalamud/Game/Gui/GameGui.cs index 3b0f6eb66..3d17aad86 100644 --- a/Dalamud/Game/Gui/GameGui.cs +++ b/Dalamud/Game/Gui/GameGui.cs @@ -32,7 +32,7 @@ namespace Dalamud.Game.Gui; [ServiceManager.EarlyLoadedService] internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("GameGui"); private readonly GameGuiAddressResolver address; diff --git a/Dalamud/Game/Gui/GameGuiAddressResolver.cs b/Dalamud/Game/Gui/GameGuiAddressResolver.cs index 1295e2047..92b89c5a9 100644 --- a/Dalamud/Game/Gui/GameGuiAddressResolver.cs +++ b/Dalamud/Game/Gui/GameGuiAddressResolver.cs @@ -1,5 +1,3 @@ -using Dalamud.Plugin.Services; - namespace Dalamud.Game.Gui; /// diff --git a/Dalamud/Game/Gui/HoverActionKind.cs b/Dalamud/Game/Gui/HoverActionKind.cs index b786f12ee..ef8fe6400 100644 --- a/Dalamud/Game/Gui/HoverActionKind.cs +++ b/Dalamud/Game/Gui/HoverActionKind.cs @@ -14,145 +14,140 @@ public enum HoverActionKind /// /// A regular action is hovered. /// - Action = 29, + Action = 28, /// /// A crafting action is hovered. /// - CraftingAction = 30, + CraftingAction = 29, /// /// A general action is hovered. /// - GeneralAction = 31, + GeneralAction = 30, /// /// A companion order type of action is hovered. /// - CompanionOrder = 32, // Game Term: BuddyOrder + CompanionOrder = 31, // Game Term: BuddyOrder /// /// A main command type of action is hovered. /// - MainCommand = 33, + MainCommand = 32, /// /// An extras command type of action is hovered. /// - ExtraCommand = 34, + ExtraCommand = 33, /// /// A companion action is hovered. /// - Companion = 35, + Companion = 34, /// /// A pet order type of action is hovered. /// - PetOrder = 36, + PetOrder = 35, /// /// A trait is hovered. /// - Trait = 37, + Trait = 36, /// /// A buddy action is hovered. /// - BuddyAction = 38, + BuddyAction = 37, /// /// A company action is hovered. /// - CompanyAction = 39, + CompanyAction = 38, /// /// A mount is hovered. /// - Mount = 40, + Mount = 39, /// /// A chocobo race action is hovered. /// - ChocoboRaceAction = 41, + ChocoboRaceAction = 40, /// /// A chocobo race item is hovered. /// - ChocoboRaceItem = 42, + ChocoboRaceItem = 41, /// /// A deep dungeon equipment is hovered. /// - DeepDungeonEquipment = 43, + DeepDungeonEquipment = 42, /// /// A deep dungeon equipment 2 is hovered. /// - DeepDungeonEquipment2 = 44, + DeepDungeonEquipment2 = 43, /// /// A deep dungeon item is hovered. /// - DeepDungeonItem = 45, + DeepDungeonItem = 44, /// /// A quick chat is hovered. /// - QuickChat = 46, + QuickChat = 45, /// /// An action combo route is hovered. /// - ActionComboRoute = 47, + ActionComboRoute = 46, /// /// A pvp trait is hovered. /// - PvPSelectTrait = 48, + PvPSelectTrait = 47, /// /// A squadron action is hovered. /// - BgcArmyAction = 49, + BgcArmyAction = 48, /// /// A perform action is hovered. /// - Perform = 50, + Perform = 49, /// /// A deep dungeon magic stone is hovered. /// - DeepDungeonMagicStone = 51, + DeepDungeonMagicStone = 50, /// /// A deep dungeon demiclone is hovered. /// - DeepDungeonDemiclone = 52, + DeepDungeonDemiclone = 51, /// /// An eureka magia action is hovered. /// - EurekaMagiaAction = 53, + EurekaMagiaAction = 52, /// /// An island sanctuary temporary item is hovered. /// - MYCTemporaryItem = 54, + MYCTemporaryItem = 53, /// /// An ornament is hovered. /// - Ornament = 55, + Ornament = 54, /// /// Glasses are hovered. /// - Glasses = 56, - - /// - /// Phantom Job Trait is hovered. - /// - MKDTrait = 58, + Glasses = 55, } diff --git a/Dalamud/Game/Gui/NamePlate/NamePlateGuiAddressResolver.cs b/Dalamud/Game/Gui/NamePlate/NamePlateGuiAddressResolver.cs index f97450c28..450e1fa9f 100644 --- a/Dalamud/Game/Gui/NamePlate/NamePlateGuiAddressResolver.cs +++ b/Dalamud/Game/Gui/NamePlate/NamePlateGuiAddressResolver.cs @@ -1,5 +1,3 @@ -using Dalamud.Plugin.Services; - namespace Dalamud.Game.Gui.NamePlate; /// diff --git a/Dalamud/Game/Gui/NamePlate/NamePlateUpdateHandler.cs b/Dalamud/Game/Gui/NamePlate/NamePlateUpdateHandler.cs index e197e2360..a8c6ff3c1 100644 --- a/Dalamud/Game/Gui/NamePlate/NamePlateUpdateHandler.cs +++ b/Dalamud/Game/Gui/NamePlate/NamePlateUpdateHandler.cs @@ -427,8 +427,8 @@ internal unsafe class NamePlateUpdateHandler : INamePlateUpdateHandler /// public int VisibilityFlags { - get => this.ObjectData->VisibilityFlags; - set => this.ObjectData->VisibilityFlags = value; + get => ObjectData->VisibilityFlags; + set => ObjectData->VisibilityFlags = value; } /// diff --git a/Dalamud/Game/Gui/PartyFinder/Types/JobFlagsExtensions.cs b/Dalamud/Game/Gui/PartyFinder/Types/JobFlagsExtensions.cs index d7ab3080b..1c78c871b 100644 --- a/Dalamud/Game/Gui/PartyFinder/Types/JobFlagsExtensions.cs +++ b/Dalamud/Game/Gui/PartyFinder/Types/JobFlagsExtensions.cs @@ -1,5 +1,4 @@ using Dalamud.Plugin.Services; - using Lumina.Excel.Sheets; namespace Dalamud.Game.Gui.PartyFinder.Types; diff --git a/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderSlot.cs b/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderSlot.cs index 953e575d4..3d1e496fc 100644 --- a/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderSlot.cs +++ b/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderSlot.cs @@ -23,7 +23,23 @@ public class PartyFinderSlot /// /// Gets a list of jobs that this slot is accepting. /// - public IReadOnlyCollection Accepting => this.listAccepting ??= Enum.GetValues().Where(flag => this[flag]).ToArray(); + public IReadOnlyCollection Accepting + { + get + { + if (this.listAccepting != null) + { + return this.listAccepting; + } + + this.listAccepting = Enum.GetValues(typeof(JobFlags)) + .Cast() + .Where(flag => this[flag]) + .ToArray(); + + return this.listAccepting; + } + } /// /// Tests if this slot is accepting a job. diff --git a/Dalamud/Game/Internal/DalamudAtkTweaks.cs b/Dalamud/Game/Internal/DalamudAtkTweaks.cs index 24fa88023..486af463c 100644 --- a/Dalamud/Game/Internal/DalamudAtkTweaks.cs +++ b/Dalamud/Game/Internal/DalamudAtkTweaks.cs @@ -22,7 +22,7 @@ namespace Dalamud.Game.Internal; [ServiceManager.EarlyLoadedService] internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("DalamudAtkTweaks"); private readonly Hook hookAgentHudOpenSystemMenu; @@ -113,7 +113,7 @@ internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService private void AtkUnitBaseReceiveGlobalEventDetour(AtkUnitBase* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { // 3 == Close - if (eventType == AtkEventType.InputReceived && WindowSystem.ShouldInhibitAtkCloseEvents && atkEventData != null && *(int*)atkEventData == 3 && this.configuration.IsFocusManagementEnabled) + if (eventType == AtkEventType.InputReceived && WindowSystem.HasAnyWindowSystemFocus && atkEventData != null && *(int*)atkEventData == 3 && this.configuration.IsFocusManagementEnabled) { Log.Verbose($"Cancelling global event SendHotkey command due to WindowSystem {WindowSystem.FocusedWindowSystemNamespace}"); return; @@ -124,7 +124,7 @@ internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService private void AgentHudOpenSystemMenuDetour(AgentHUD* thisPtr, AtkValue* atkValueArgs, uint menuSize) { - if (WindowSystem.ShouldInhibitAtkCloseEvents && this.configuration.IsFocusManagementEnabled) + if (WindowSystem.HasAnyWindowSystemFocus && this.configuration.IsFocusManagementEnabled) { Log.Verbose($"Cancelling OpenSystemMenu due to WindowSystem {WindowSystem.FocusedWindowSystemNamespace}"); return; diff --git a/Dalamud/Game/Internal/DalamudCompletion.cs b/Dalamud/Game/Internal/DalamudCompletion.cs index 50816a603..e3564c823 100644 --- a/Dalamud/Game/Internal/DalamudCompletion.cs +++ b/Dalamud/Game/Internal/DalamudCompletion.cs @@ -11,6 +11,8 @@ using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Component.Completion; using FFXIVClientStructs.FFXIV.Component.GUI; +using Lumina.Text; + namespace Dalamud.Game.Internal; /// @@ -251,14 +253,16 @@ internal sealed unsafe class DalamudCompletion : IInternalDisposableService { public EntryStrings(string command) { - using var rssb = new RentedSeStringBuilder(); + var rssb = SeStringBuilder.SharedPool.Get(); - this.Display = Utf8String.FromSequence(rssb.Builder + this.Display = Utf8String.FromSequence(rssb .PushColorType(539) .Append(command) .PopColorType() .GetViewAsSpan()); + SeStringBuilder.SharedPool.Return(rssb); + this.Match = Utf8String.FromString(command); } diff --git a/Dalamud/Game/Inventory/GameInventory.cs b/Dalamud/Game/Inventory/GameInventory.cs index 2cfacfad0..535b84372 100644 --- a/Dalamud/Game/Inventory/GameInventory.cs +++ b/Dalamud/Game/Inventory/GameInventory.cs @@ -18,15 +18,15 @@ namespace Dalamud.Game.Inventory; [ServiceManager.EarlyLoadedService] internal class GameInventory : IInternalDisposableService { - private readonly List subscribersPendingChange = []; - private readonly List subscribers = []; + private readonly List subscribersPendingChange = new(); + private readonly List subscribers = new(); - private readonly List addedEvents = []; - private readonly List removedEvents = []; - private readonly List changedEvents = []; - private readonly List movedEvents = []; - private readonly List splitEvents = []; - private readonly List mergedEvents = []; + private readonly List addedEvents = new(); + private readonly List removedEvents = new(); + private readonly List changedEvents = new(); + private readonly List movedEvents = new(); + private readonly List splitEvents = new(); + private readonly List mergedEvents = new(); [ServiceManager.ServiceDependency] private readonly Framework framework = Service.Get(); @@ -151,7 +151,7 @@ internal class GameInventory : IInternalDisposableService bool isNew; lock (this.subscribersPendingChange) { - isNew = this.subscribersPendingChange.Count != 0 && this.subscribers.Count == 0; + isNew = this.subscribersPendingChange.Any() && !this.subscribers.Any(); this.subscribers.Clear(); this.subscribers.AddRange(this.subscribersPendingChange); this.subscribersChanged = false; @@ -305,8 +305,7 @@ internal class GameInventory : IInternalDisposableService private GameInventoryItem[] CreateItemsArray(int length) { var items = new GameInventoryItem[length]; - foreach (ref var item in items.AsSpan()) - item = new(); + items.Initialize(); return items; } @@ -348,7 +347,7 @@ internal class GameInventory : IInternalDisposableService #pragma warning restore SA1015 internal class GameInventoryPluginScoped : IInternalDisposableService, IGameInventory { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new(nameof(GameInventoryPluginScoped)); [ServiceManager.ServiceDependency] private readonly GameInventory gameInventoryService = Service.Get(); diff --git a/Dalamud/Game/Marketboard/MarketBoard.cs b/Dalamud/Game/Marketboard/MarketBoard.cs index 563a5bc4a..962e0010e 100644 --- a/Dalamud/Game/Marketboard/MarketBoard.cs +++ b/Dalamud/Game/Marketboard/MarketBoard.cs @@ -1,3 +1,5 @@ +using System.Linq; + using Dalamud.Game.Network.Internal; using Dalamud.Game.Network.Structures; using Dalamud.IoC; @@ -93,7 +95,7 @@ internal class MarketBoard : IInternalDisposableService, IMarketBoard #pragma warning restore SA1015 internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoard { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new(nameof(MarketBoardPluginScoped)); [ServiceManager.ServiceDependency] private readonly MarketBoard marketBoardService = Service.Get(); diff --git a/Dalamud/Game/NativeWrapper/AgentInterfacePtr.cs b/Dalamud/Game/NativeWrapper/AgentInterfacePtr.cs index b5e8938dd..b5a6375a9 100644 --- a/Dalamud/Game/NativeWrapper/AgentInterfacePtr.cs +++ b/Dalamud/Game/NativeWrapper/AgentInterfacePtr.cs @@ -81,7 +81,7 @@ public readonly unsafe struct AgentInterfacePtr(nint address) : IEquatable /// true when the addon was focused, false otherwise. - public readonly bool FocusAddon() => !this.IsNull && this.Struct->FocusAddon(); + public readonly bool FocusAddon() => this.IsNull && this.Struct->FocusAddon(); /// Determines whether the specified AgentInterfacePtr is equal to the current AgentInterfacePtr. /// The AgentInterfacePtr to compare with the current AgentInterfacePtr. diff --git a/Dalamud/Game/NativeWrapper/AtkValuePtr.cs b/Dalamud/Game/NativeWrapper/AtkValuePtr.cs index b274d388b..a47483a66 100644 --- a/Dalamud/Game/NativeWrapper/AtkValuePtr.cs +++ b/Dalamud/Game/NativeWrapper/AtkValuePtr.cs @@ -89,7 +89,7 @@ public readonly unsafe struct AtkValuePtr(nint address) : IEquatable public unsafe bool TryGet([NotNullWhen(true)] out T? result) where T : struct { - var value = this.GetValue(); + object? value = this.GetValue(); if (value is T typed) { result = typed; diff --git a/Dalamud/Game/Network/GameNetwork.cs b/Dalamud/Game/Network/GameNetwork.cs new file mode 100644 index 000000000..be464ef34 --- /dev/null +++ b/Dalamud/Game/Network/GameNetwork.cs @@ -0,0 +1,150 @@ +using System.Runtime.InteropServices; + +using Dalamud.Configuration.Internal; +using Dalamud.Hooking; +using Dalamud.IoC; +using Dalamud.IoC.Internal; +using Dalamud.Plugin.Services; +using Dalamud.Utility; + +using FFXIVClientStructs.FFXIV.Client.Network; + +using Serilog; + +namespace Dalamud.Game.Network; + +/// +/// This class handles interacting with game network events. +/// +[ServiceManager.EarlyLoadedService] +internal sealed unsafe class GameNetwork : IInternalDisposableService +{ + private readonly GameNetworkAddressResolver address; + private readonly Hook processZonePacketDownHook; + private readonly Hook processZonePacketUpHook; + + private readonly HitchDetector hitchDetectorUp; + private readonly HitchDetector hitchDetectorDown; + + [ServiceManager.ServiceDependency] + private readonly DalamudConfiguration configuration = Service.Get(); + + [ServiceManager.ServiceConstructor] + private unsafe GameNetwork(TargetSigScanner sigScanner) + { + this.hitchDetectorUp = new HitchDetector("GameNetworkUp", this.configuration.GameNetworkUpHitch); + this.hitchDetectorDown = new HitchDetector("GameNetworkDown", this.configuration.GameNetworkDownHitch); + + this.address = new GameNetworkAddressResolver(); + this.address.Setup(sigScanner); + + var onReceivePacketAddress = (nint)PacketDispatcher.StaticVirtualTablePointer->OnReceivePacket; + + Log.Verbose("===== G A M E N E T W O R K ====="); + Log.Verbose($"OnReceivePacket address {Util.DescribeAddress(onReceivePacketAddress)}"); + Log.Verbose($"ProcessZonePacketUp address {Util.DescribeAddress(this.address.ProcessZonePacketUp)}"); + + this.processZonePacketDownHook = Hook.FromAddress(onReceivePacketAddress, this.ProcessZonePacketDownDetour); + this.processZonePacketUpHook = Hook.FromAddress(this.address.ProcessZonePacketUp, this.ProcessZonePacketUpDetour); + + this.processZonePacketDownHook.Enable(); + this.processZonePacketUpHook.Enable(); + } + + /// + /// The delegate type of a network message event. + /// + /// The pointer to the raw data. + /// The operation ID code. + /// The source actor ID. + /// The taret actor ID. + /// The direction of the packed. + public delegate void OnNetworkMessageDelegate(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + private delegate byte ProcessZonePacketUpDelegate(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4); + + /// + /// Event that is called when a network message is sent/received. + /// + public event OnNetworkMessageDelegate? NetworkMessage; + + /// + void IInternalDisposableService.DisposeService() + { + this.processZonePacketDownHook.Dispose(); + this.processZonePacketUpHook.Dispose(); + } + + private void ProcessZonePacketDownDetour(PacketDispatcher* dispatcher, uint targetId, IntPtr dataPtr) + { + this.hitchDetectorDown.Start(); + + // Go back 0x10 to get back to the start of the packet header + dataPtr -= 0x10; + + foreach (var d in Delegate.EnumerateInvocationList(this.NetworkMessage)) + { + try + { + d.Invoke( + dataPtr + 0x20, + (ushort)Marshal.ReadInt16(dataPtr, 0x12), + 0, + targetId, + NetworkMessageDirection.ZoneDown); + } + catch (Exception ex) + { + string header; + try + { + var data = new byte[32]; + Marshal.Copy(dataPtr, data, 0, 32); + header = BitConverter.ToString(data); + } + catch (Exception) + { + header = "failed"; + } + + Log.Error(ex, "Exception on ProcessZonePacketDown hook. Header: " + header); + } + } + + this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10); + this.hitchDetectorDown.Stop(); + } + + private byte ProcessZonePacketUpDetour(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4) + { + this.hitchDetectorUp.Start(); + + try + { + // Call events + // TODO: Implement actor IDs + this.NetworkMessage?.Invoke(dataPtr + 0x20, (ushort)Marshal.ReadInt16(dataPtr), 0x0, 0x0, NetworkMessageDirection.ZoneUp); + } + catch (Exception ex) + { + string header; + try + { + var data = new byte[32]; + Marshal.Copy(dataPtr, data, 0, 32); + header = BitConverter.ToString(data); + } + catch (Exception) + { + header = "failed"; + } + + Log.Error(ex, "Exception on ProcessZonePacketUp hook. Header: " + header); + } + + this.hitchDetectorUp.Stop(); + + return this.processZonePacketUpHook.Original(a1, dataPtr, a3, a4); + } +} diff --git a/Dalamud/Game/Network/GameNetworkAddressResolver.cs b/Dalamud/Game/Network/GameNetworkAddressResolver.cs new file mode 100644 index 000000000..de92f7c10 --- /dev/null +++ b/Dalamud/Game/Network/GameNetworkAddressResolver.cs @@ -0,0 +1,18 @@ +namespace Dalamud.Game.Network; + +/// +/// The address resolver for the class. +/// +internal sealed class GameNetworkAddressResolver : BaseAddressResolver +{ + /// + /// Gets the address of the ProcessZonePacketUp method. + /// + public IntPtr ProcessZonePacketUp { get; private set; } + + /// + protected override void Setup64Bit(ISigScanner sig) + { + this.ProcessZonePacketUp = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 4C 89 64 24 ?? 55 41 56 41 57 48 8B EC 48 83 EC 70"); // unnamed in cs + } +} diff --git a/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs b/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs index 67769e4ca..7ecb9c397 100644 --- a/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs +++ b/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Net.Http; using System.Text; using System.Threading.Tasks; @@ -7,7 +8,6 @@ using Dalamud.Game.Network.Structures; using Dalamud.Networking.Http; using Newtonsoft.Json; - using Serilog; namespace Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis; @@ -64,7 +64,7 @@ internal class UniversalisMarketBoardUploader : IMarketBoardUploader PricePerUnit = marketBoardItemListing.PricePerUnit, Quantity = marketBoardItemListing.ItemQuantity, RetainerCity = marketBoardItemListing.RetainerCityId, - Materia = [], + Materia = new List(), }; #pragma warning restore CS0618 // Type or member is obsolete diff --git a/Dalamud/Game/Network/Internal/NetworkHandlers.cs b/Dalamud/Game/Network/Internal/NetworkHandlers.cs index d3a53b4f2..6a6d73b33 100644 --- a/Dalamud/Game/Network/Internal/NetworkHandlers.cs +++ b/Dalamud/Game/Network/Internal/NetworkHandlers.cs @@ -33,7 +33,7 @@ namespace Dalamud.Game.Network.Internal; [ServiceManager.EarlyLoadedService] internal unsafe class NetworkHandlers : IInternalDisposableService { - private readonly UniversalisMarketBoardUploader uploader; + private readonly IMarketBoardUploader uploader; private readonly IDisposable handleMarketBoardItemRequest; private readonly IDisposable handleMarketTaxRates; @@ -55,7 +55,10 @@ internal unsafe class NetworkHandlers : IInternalDisposableService private bool disposing; [ServiceManager.ServiceConstructor] - private NetworkHandlers(TargetSigScanner sigScanner, HappyHttpClient happyHttpClient) + private NetworkHandlers( + GameNetwork gameNetwork, + TargetSigScanner sigScanner, + HappyHttpClient happyHttpClient) { this.uploader = new UniversalisMarketBoardUploader(happyHttpClient); @@ -416,7 +419,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService private IDisposable HandleMarketBoardItemRequest() { - static void LogStartObserved(MarketBoardItemRequest request) + void LogStartObserved(MarketBoardItemRequest request) { Log.Verbose("Observed start of request for item with {NumListings} expected listings", request.AmountToArrive); } @@ -445,7 +448,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService private void UploadMarketBoardData( MarketBoardItemRequest request, (uint CatalogId, ICollection Sales) sales, - List listings, + ICollection listings, ulong uploaderId, uint worldId) { diff --git a/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs b/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs index 34c071556..9cd46f798 100644 --- a/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs +++ b/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs @@ -1,6 +1,4 @@ -using Dalamud.Plugin.Services; - -namespace Dalamud.Game.Network.Internal; +namespace Dalamud.Game.Network.Internal; /// /// Internal address resolver for the network handlers. diff --git a/Dalamud/Game/Network/NetworkMessageDirection.cs b/Dalamud/Game/Network/NetworkMessageDirection.cs index 12cfc3d17..87cce5173 100644 --- a/Dalamud/Game/Network/NetworkMessageDirection.cs +++ b/Dalamud/Game/Network/NetworkMessageDirection.cs @@ -3,7 +3,6 @@ namespace Dalamud.Game.Network; /// /// This represents the direction of a network message. /// -[Obsolete("No longer part of public API", true)] public enum NetworkMessageDirection { /// diff --git a/Dalamud/Game/Player/PlayerState.cs b/Dalamud/Game/Player/PlayerState.cs index bd19b5bfb..917c946db 100644 --- a/Dalamud/Game/Player/PlayerState.cs +++ b/Dalamud/Game/Player/PlayerState.cs @@ -77,7 +77,7 @@ internal unsafe class PlayerState : IServiceType, IPlayerState public RowRef ClassJob => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->CurrentClassJobId) : default; /// - public short Level => this.IsLoaded && this.ClassJob.IsValid ? this.GetClassJobLevel(this.ClassJob.Value) : this.EffectiveLevel; + public short Level => this.IsLoaded ? CSPlayerState.Instance()->CurrentLevel : default; /// public bool IsLevelSynced => this.IsLoaded && CSPlayerState.Instance()->IsLevelSynced; diff --git a/Dalamud/Game/SigScanner.cs b/Dalamud/Game/SigScanner.cs index 81fb8a3a3..c8a371aee 100644 --- a/Dalamud/Game/SigScanner.cs +++ b/Dalamud/Game/SigScanner.cs @@ -8,12 +8,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; -using Dalamud.Plugin.Services; - using Iced.Intel; - using Newtonsoft.Json; - using Serilog; namespace Dalamud.Game; diff --git a/Dalamud/Game/TargetSigScanner.cs b/Dalamud/Game/TargetSigScanner.cs index 540d0ea47..f60c32d9a 100644 --- a/Dalamud/Game/TargetSigScanner.cs +++ b/Dalamud/Game/TargetSigScanner.cs @@ -1,9 +1,8 @@ -using System.Diagnostics; +using System.Diagnostics; using System.IO; using Dalamud.IoC; using Dalamud.IoC.Internal; -using Dalamud.Plugin.Services; namespace Dalamud.Game; diff --git a/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs b/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs index 58b9011e6..58bcdbd0b 100644 --- a/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs +++ b/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs @@ -48,7 +48,7 @@ namespace Dalamud.Game.Text.Evaluator; [ResolveVia] internal class SeStringEvaluator : IServiceType, ISeStringEvaluator { - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("SeStringEvaluator"); [ServiceManager.ServiceDependency] private readonly ClientState.ClientState clientState = Service.Get(); @@ -102,15 +102,16 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator // TODO: remove culture info toggling after supporting CultureInfo for SeStringBuilder.Append, // and then remove try...finally block (discard builder from the pool on exception) var previousCulture = CultureInfo.CurrentCulture; - using var rssb = new RentedSeStringBuilder(); + var builder = SeStringBuilder.SharedPool.Get(); try { CultureInfo.CurrentCulture = Localization.GetCultureInfoFromLangCode(lang.ToCode()); - return this.EvaluateAndAppendTo(rssb.Builder, str, localParameters, lang).ToReadOnlySeString(); + return this.EvaluateAndAppendTo(builder, str, localParameters, lang).ToReadOnlySeString(); } finally { CultureInfo.CurrentCulture = previousCulture; + SeStringBuilder.SharedPool.Return(builder); } } @@ -244,67 +245,154 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator // if (context.HandlePayload(payload, in context)) // return true; - return payload.MacroCode switch + switch (payload.MacroCode) { - MacroCode.SetResetTime => this.TryResolveSetResetTime(in context, payload), - MacroCode.SetTime => this.TryResolveSetTime(in context, payload), - MacroCode.If => this.TryResolveIf(in context, payload), - MacroCode.Switch => this.TryResolveSwitch(in context, payload), - MacroCode.SwitchPlatform => this.TryResolveSwitchPlatform(in context, payload), - MacroCode.PcName => this.TryResolvePcName(in context, payload), - MacroCode.IfPcGender => this.TryResolveIfPcGender(in context, payload), - MacroCode.IfPcName => this.TryResolveIfPcName(in context, payload), - // MacroCode.Josa - // MacroCode.Josaro - MacroCode.IfSelf => this.TryResolveIfSelf(in context, payload), - // MacroCode.NewLine (pass through) - // MacroCode.Wait (pass through) - // MacroCode.Icon (pass through) - MacroCode.Color => this.TryResolveColor(in context, payload), - MacroCode.EdgeColor => this.TryResolveEdgeColor(in context, payload), - MacroCode.ShadowColor => this.TryResolveShadowColor(in context, payload), - // MacroCode.SoftHyphen (pass through) - // MacroCode.Key - // MacroCode.Scale - MacroCode.Bold => this.TryResolveBold(in context, payload), - MacroCode.Italic => this.TryResolveItalic(in context, payload), - // MacroCode.Edge - // MacroCode.Shadow - // MacroCode.NonBreakingSpace (pass through) - // MacroCode.Icon2 (pass through) - // MacroCode.Hyphen (pass through) - MacroCode.Num => this.TryResolveNum(in context, payload), - MacroCode.Hex => this.TryResolveHex(in context, payload), - MacroCode.Kilo => this.TryResolveKilo(in context, payload), - // MacroCode.Byte - MacroCode.Sec => this.TryResolveSec(in context, payload), - // MacroCode.Time - MacroCode.Float => this.TryResolveFloat(in context, payload), - // MacroCode.Link (pass through) - MacroCode.Sheet => this.TryResolveSheet(in context, payload), - MacroCode.SheetSub => this.TryResolveSheetSub(in context, payload), - MacroCode.String => this.TryResolveString(in context, payload), - MacroCode.Caps => this.TryResolveCaps(in context, payload), - MacroCode.Head => this.TryResolveHead(in context, payload), - MacroCode.Split => this.TryResolveSplit(in context, payload), - MacroCode.HeadAll => this.TryResolveHeadAll(in context, payload), - MacroCode.Fixed => this.TryResolveFixed(in context, payload), - MacroCode.Lower => this.TryResolveLower(in context, payload), - MacroCode.JaNoun => this.TryResolveNoun(ClientLanguage.Japanese, in context, payload), - MacroCode.EnNoun => this.TryResolveNoun(ClientLanguage.English, in context, payload), - MacroCode.DeNoun => this.TryResolveNoun(ClientLanguage.German, in context, payload), - MacroCode.FrNoun => this.TryResolveNoun(ClientLanguage.French, in context, payload), - // MacroCode.ChNoun - MacroCode.LowerHead => this.TryResolveLowerHead(in context, payload), - MacroCode.ColorType => this.TryResolveColorType(in context, payload), - MacroCode.EdgeColorType => this.TryResolveEdgeColorType(in context, payload), - // MacroCode.Ruby - MacroCode.Digit => this.TryResolveDigit(in context, payload), - MacroCode.Ordinal => this.TryResolveOrdinal(in context, payload), - // MacroCode.Sound (pass through) - MacroCode.LevelPos => this.TryResolveLevelPos(in context, payload), - _ => false, - }; + case MacroCode.SetResetTime: + return this.TryResolveSetResetTime(in context, payload); + + case MacroCode.SetTime: + return this.TryResolveSetTime(in context, payload); + + case MacroCode.If: + return this.TryResolveIf(in context, payload); + + case MacroCode.Switch: + return this.TryResolveSwitch(in context, payload); + + case MacroCode.SwitchPlatform: + return this.TryResolveSwitchPlatform(in context, payload); + + case MacroCode.PcName: + return this.TryResolvePcName(in context, payload); + + case MacroCode.IfPcGender: + return this.TryResolveIfPcGender(in context, payload); + + case MacroCode.IfPcName: + return this.TryResolveIfPcName(in context, payload); + + // case MacroCode.Josa: + // case MacroCode.Josaro: + + case MacroCode.IfSelf: + return this.TryResolveIfSelf(in context, payload); + + // case MacroCode.NewLine: // pass through + // case MacroCode.Wait: // pass through + // case MacroCode.Icon: // pass through + + case MacroCode.Color: + return this.TryResolveColor(in context, payload); + + case MacroCode.EdgeColor: + return this.TryResolveEdgeColor(in context, payload); + + case MacroCode.ShadowColor: + return this.TryResolveShadowColor(in context, payload); + + // case MacroCode.SoftHyphen: // pass through + // case MacroCode.Key: + // case MacroCode.Scale: + + case MacroCode.Bold: + return this.TryResolveBold(in context, payload); + + case MacroCode.Italic: + return this.TryResolveItalic(in context, payload); + + // case MacroCode.Edge: + // case MacroCode.Shadow: + // case MacroCode.NonBreakingSpace: // pass through + // case MacroCode.Icon2: // pass through + // case MacroCode.Hyphen: // pass through + + case MacroCode.Num: + return this.TryResolveNum(in context, payload); + + case MacroCode.Hex: + return this.TryResolveHex(in context, payload); + + case MacroCode.Kilo: + return this.TryResolveKilo(in context, payload); + + // case MacroCode.Byte: + + case MacroCode.Sec: + return this.TryResolveSec(in context, payload); + + // case MacroCode.Time: + + case MacroCode.Float: + return this.TryResolveFloat(in context, payload); + + // case MacroCode.Link: // pass through + + case MacroCode.Sheet: + return this.TryResolveSheet(in context, payload); + + case MacroCode.SheetSub: + return this.TryResolveSheetSub(in context, payload); + + case MacroCode.String: + return this.TryResolveString(in context, payload); + + case MacroCode.Caps: + return this.TryResolveCaps(in context, payload); + + case MacroCode.Head: + return this.TryResolveHead(in context, payload); + + case MacroCode.Split: + return this.TryResolveSplit(in context, payload); + + case MacroCode.HeadAll: + return this.TryResolveHeadAll(in context, payload); + + case MacroCode.Fixed: + return this.TryResolveFixed(in context, payload); + + case MacroCode.Lower: + return this.TryResolveLower(in context, payload); + + case MacroCode.JaNoun: + return this.TryResolveNoun(ClientLanguage.Japanese, in context, payload); + + case MacroCode.EnNoun: + return this.TryResolveNoun(ClientLanguage.English, in context, payload); + + case MacroCode.DeNoun: + return this.TryResolveNoun(ClientLanguage.German, in context, payload); + + case MacroCode.FrNoun: + return this.TryResolveNoun(ClientLanguage.French, in context, payload); + + // case MacroCode.ChNoun: + + case MacroCode.LowerHead: + return this.TryResolveLowerHead(in context, payload); + + case MacroCode.ColorType: + return this.TryResolveColorType(in context, payload); + + case MacroCode.EdgeColorType: + return this.TryResolveEdgeColorType(in context, payload); + + // case MacroCode.Ruby: + + case MacroCode.Digit: + return this.TryResolveDigit(in context, payload); + + case MacroCode.Ordinal: + return this.TryResolveOrdinal(in context, payload); + + // case MacroCode.Sound: // pass through + + case MacroCode.LevelPos: + return this.TryResolveLevelPos(in context, payload); + + default: + return false; + } } private unsafe bool TryResolveSetResetTime(in SeStringContext context, in ReadOnlySePayloadSpan payload) @@ -842,10 +930,9 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator itemId += 1000000; } - using var rssb = new RentedSeStringBuilder(); - var sb = rssb.Builder; + var sb = SeStringBuilder.SharedPool.Get(); - sb.Append(this.EvaluateFromAddon(6, [rarity], context.Language)); // appends colortype and edgecolortype + sb.Append(this.EvaluateFromAddon(6, [rarity], context.Language)); if (!skipLink) sb.PushLink(LinkMacroPayloadType.Item, itemId, rarity, 0u); // arg3 = some LogMessage flag based on LogKind RowId? => "89 5C 24 20 E8 ?? ?? ?? ?? 48 8B 1F" @@ -868,10 +955,8 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator if (!skipLink) sb.PopLink(); - sb.PopEdgeColorType(); - sb.PopColorType(); - text = sb.ToReadOnlySeString(); + SeStringBuilder.SharedPool.Return(sb); } private void CreateSheetLink(in SeStringContext context, string resolvedSheetName, ReadOnlySeString text, uint eRowIdValue, uint eColParamValue) @@ -943,33 +1028,40 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator if (!payload.TryGetExpression(out var eStr)) return false; - using var rssb = new RentedSeStringBuilder(); + var builder = SeStringBuilder.SharedPool.Get(); - var headContext = new SeStringContext(rssb.Builder, context.LocalParameters, context.Language); - - if (!this.ResolveStringExpression(headContext, eStr)) - return false; - - var str = rssb.Builder.ToReadOnlySeString(); - var pIdx = 0; - - foreach (var p in str) + try { - pIdx++; + var headContext = new SeStringContext(builder, context.LocalParameters, context.Language); - if (p.Type == ReadOnlySePayloadType.Invalid) - continue; + if (!this.ResolveStringExpression(headContext, eStr)) + return false; - if (pIdx == 1 && p.Type == ReadOnlySePayloadType.Text) + var str = builder.ToReadOnlySeString(); + var pIdx = 0; + + foreach (var p in str) { - context.Builder.Append(Encoding.UTF8.GetString(p.Body.ToArray()).ToUpper(context.CultureInfo)); - continue; + pIdx++; + + if (p.Type == ReadOnlySePayloadType.Invalid) + continue; + + if (pIdx == 1 && p.Type == ReadOnlySePayloadType.Text) + { + context.Builder.Append(Encoding.UTF8.GetString(p.Body.ToArray()).ToUpper(context.CultureInfo)); + continue; + } + + context.Builder.Append(p); } - context.Builder.Append(p); + return true; + } + finally + { + SeStringBuilder.SharedPool.Return(builder); } - - return true; } private bool TryResolveHead(in SeStringContext context, in ReadOnlySePayloadSpan payload) @@ -977,33 +1069,40 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator if (!payload.TryGetExpression(out var eStr)) return false; - using var rssb = new RentedSeStringBuilder(); + var builder = SeStringBuilder.SharedPool.Get(); - var headContext = new SeStringContext(rssb.Builder, context.LocalParameters, context.Language); - - if (!this.ResolveStringExpression(headContext, eStr)) - return false; - - var str = rssb.Builder.ToReadOnlySeString(); - var pIdx = 0; - - foreach (var p in str) + try { - pIdx++; + var headContext = new SeStringContext(builder, context.LocalParameters, context.Language); - if (p.Type == ReadOnlySePayloadType.Invalid) - continue; + if (!this.ResolveStringExpression(headContext, eStr)) + return false; - if (pIdx == 1 && p.Type == ReadOnlySePayloadType.Text) + var str = builder.ToReadOnlySeString(); + var pIdx = 0; + + foreach (var p in str) { - context.Builder.Append(Encoding.UTF8.GetString(p.Body.Span).FirstCharToUpper(context.CultureInfo)); - continue; + pIdx++; + + if (p.Type == ReadOnlySePayloadType.Invalid) + continue; + + if (pIdx == 1 && p.Type == ReadOnlySePayloadType.Text) + { + context.Builder.Append(Encoding.UTF8.GetString(p.Body.Span).FirstCharToUpper(context.CultureInfo)); + continue; + } + + context.Builder.Append(p); } - context.Builder.Append(p); + return true; + } + finally + { + SeStringBuilder.SharedPool.Return(builder); } - - return true; } private bool TryResolveSplit(in SeStringContext context, in ReadOnlySePayloadSpan payload) @@ -1014,25 +1113,32 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator if (!eSeparator.TryGetString(out var eSeparatorVal) || !eIndex.TryGetUInt(out var eIndexVal) || eIndexVal <= 0) return false; - using var rssb = new RentedSeStringBuilder(); + var builder = SeStringBuilder.SharedPool.Get(); - var headContext = new SeStringContext(rssb.Builder, context.LocalParameters, context.Language); - - if (!this.ResolveStringExpression(headContext, eText)) - return false; - - var separator = eSeparatorVal.ExtractText(); - if (separator.Length < 1) - return false; - - var splitted = rssb.Builder.ToReadOnlySeString().ExtractText().Split(separator[0]); - if (eIndexVal <= splitted.Length) + try { - context.Builder.Append(splitted[eIndexVal - 1]); - return true; - } + var headContext = new SeStringContext(builder, context.LocalParameters, context.Language); - return false; + if (!this.ResolveStringExpression(headContext, eText)) + return false; + + var separator = eSeparatorVal.ExtractText(); + if (separator.Length < 1) + return false; + + var splitted = builder.ToReadOnlySeString().ExtractText().Split(separator[0]); + if (eIndexVal <= splitted.Length) + { + context.Builder.Append(splitted[eIndexVal - 1]); + return true; + } + + return false; + } + finally + { + SeStringBuilder.SharedPool.Return(builder); + } } private bool TryResolveHeadAll(in SeStringContext context, in ReadOnlySePayloadSpan payload) @@ -1040,30 +1146,37 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator if (!payload.TryGetExpression(out var eStr)) return false; - using var rssb = new RentedSeStringBuilder(); + var builder = SeStringBuilder.SharedPool.Get(); - var headContext = new SeStringContext(rssb.Builder, context.LocalParameters, context.Language); - - if (!this.ResolveStringExpression(headContext, eStr)) - return false; - - var str = rssb.Builder.ToReadOnlySeString(); - - foreach (var p in str) + try { - if (p.Type == ReadOnlySePayloadType.Invalid) - continue; + var headContext = new SeStringContext(builder, context.LocalParameters, context.Language); - if (p.Type == ReadOnlySePayloadType.Text) + if (!this.ResolveStringExpression(headContext, eStr)) + return false; + + var str = builder.ToReadOnlySeString(); + + foreach (var p in str) { - context.Builder.Append(Encoding.UTF8.GetString(p.Body.Span).ToUpper(true, true, false, context.Language)); - continue; + if (p.Type == ReadOnlySePayloadType.Invalid) + continue; + + if (p.Type == ReadOnlySePayloadType.Text) + { + context.Builder.Append(Encoding.UTF8.GetString(p.Body.Span).ToUpper(true, true, false, context.Language)); + continue; + } + + context.Builder.Append(p); } - context.Builder.Append(p); + return true; + } + finally + { + SeStringBuilder.SharedPool.Return(builder); } - - return true; } private bool TryResolveFixed(in SeStringContext context, in ReadOnlySePayloadSpan payload) @@ -1193,13 +1306,14 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator if (!this.dataManager.GetExcelSheet().TryGetRow(mapId, out var mapRow)) return false; - using var rssb = new RentedSeStringBuilder(); + var sb = SeStringBuilder.SharedPool.Get(); - rssb.Builder.Append(placeNameRow.Name); + sb.Append(placeNameRow.Name); if (instance is > 0 and <= 9) - rssb.Builder.Append((char)((char)0xE0B0 + (char)instance)); + sb.Append((char)((char)0xE0B0 + (char)instance)); - var placeNameWithInstance = rssb.Builder.ToReadOnlySeString(); + var placeNameWithInstance = sb.ToReadOnlySeString(); + SeStringBuilder.SharedPool.Return(sb); var mapPosX = ConvertRawToMapPosX(mapRow, rawX / 1000f); var mapPosY = ConvertRawToMapPosY(mapRow, rawY / 1000f); @@ -1348,22 +1462,23 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator statusDescription = statusRow.Description.AsSpan(); } - using var rssb = new RentedSeStringBuilder(); + var sb = SeStringBuilder.SharedPool.Get(); switch (statusRow.StatusCategory) { case 1: - rssb.Builder.Append(this.EvaluateFromAddon(376, default, context.Language)); + sb.Append(this.EvaluateFromAddon(376, default, context.Language)); break; case 2: - rssb.Builder.Append(this.EvaluateFromAddon(377, default, context.Language)); + sb.Append(this.EvaluateFromAddon(377, default, context.Language)); break; } - rssb.Builder.Append(statusName); + sb.Append(statusName); - var linkText = rssb.Builder.ToReadOnlySeString(); + var linkText = sb.ToReadOnlySeString(); + SeStringBuilder.SharedPool.Return(sb); context.Builder .BeginMacro(MacroCode.Link) @@ -1618,31 +1733,38 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator if (!payload.TryGetExpression(out var eStr)) return false; - using var rssb = new RentedSeStringBuilder(); + var builder = SeStringBuilder.SharedPool.Get(); - var headContext = new SeStringContext(rssb.Builder, context.LocalParameters, context.Language); - - if (!this.ResolveStringExpression(headContext, eStr)) - return false; - - var str = rssb.Builder.ToReadOnlySeString(); - - foreach (var p in str) + try { - if (p.Type == ReadOnlySePayloadType.Invalid) - continue; + var headContext = new SeStringContext(builder, context.LocalParameters, context.Language); - if (p.Type == ReadOnlySePayloadType.Text) + if (!this.ResolveStringExpression(headContext, eStr)) + return false; + + var str = builder.ToReadOnlySeString(); + + foreach (var p in str) { - context.Builder.Append(Encoding.UTF8.GetString(p.Body.ToArray()).ToLower(context.CultureInfo)); + if (p.Type == ReadOnlySePayloadType.Invalid) + continue; - continue; + if (p.Type == ReadOnlySePayloadType.Text) + { + context.Builder.Append(Encoding.UTF8.GetString(p.Body.ToArray()).ToLower(context.CultureInfo)); + + continue; + } + + context.Builder.Append(p); } - context.Builder.Append(p); + return true; + } + finally + { + SeStringBuilder.SharedPool.Return(builder); } - - return true; } private bool TryResolveNoun(ClientLanguage language, in SeStringContext context, in ReadOnlySePayloadSpan payload) @@ -1712,33 +1834,40 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator if (!payload.TryGetExpression(out var eStr)) return false; - using var rssb = new RentedSeStringBuilder(); + var builder = SeStringBuilder.SharedPool.Get(); - var headContext = new SeStringContext(rssb.Builder, context.LocalParameters, context.Language); - - if (!this.ResolveStringExpression(headContext, eStr)) - return false; - - var str = rssb.Builder.ToReadOnlySeString(); - var pIdx = 0; - - foreach (var p in str) + try { - pIdx++; + var headContext = new SeStringContext(builder, context.LocalParameters, context.Language); - if (p.Type == ReadOnlySePayloadType.Invalid) - continue; + if (!this.ResolveStringExpression(headContext, eStr)) + return false; - if (pIdx == 1 && p.Type == ReadOnlySePayloadType.Text) + var str = builder.ToReadOnlySeString(); + var pIdx = 0; + + foreach (var p in str) { - context.Builder.Append(Encoding.UTF8.GetString(p.Body.Span).FirstCharToLower(context.CultureInfo)); - continue; + pIdx++; + + if (p.Type == ReadOnlySePayloadType.Invalid) + continue; + + if (pIdx == 1 && p.Type == ReadOnlySePayloadType.Text) + { + context.Builder.Append(Encoding.UTF8.GetString(p.Body.Span).FirstCharToLower(context.CultureInfo)); + continue; + } + + context.Builder.Append(p); } - context.Builder.Append(p); + return true; + } + finally + { + SeStringBuilder.SharedPool.Return(builder); } - - return true; } private bool TryResolveColorType(in SeStringContext context, in ReadOnlySePayloadSpan payload) @@ -2003,19 +2132,19 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator if (operand1.TryGetString(out var strval1) && operand2.TryGetString(out var strval2)) { - using var rssb1 = new RentedSeStringBuilder(); - using var rssb2 = new RentedSeStringBuilder(); var resolvedStr1 = this.EvaluateAndAppendTo( - rssb1.Builder, + SeStringBuilder.SharedPool.Get(), strval1, context.LocalParameters, context.Language); var resolvedStr2 = this.EvaluateAndAppendTo( - rssb2.Builder, + SeStringBuilder.SharedPool.Get(), strval2, context.LocalParameters, context.Language); var equals = resolvedStr1.GetViewAsSpan().SequenceEqual(resolvedStr2.GetViewAsSpan()); + SeStringBuilder.SharedPool.Return(resolvedStr1); + SeStringBuilder.SharedPool.Return(resolvedStr2); if ((ExpressionType)exprType == ExpressionType.Equal) value = equals ? 1u : 0u; diff --git a/Dalamud/Game/Text/Evaluator/SeStringParameter.cs b/Dalamud/Game/Text/Evaluator/SeStringParameter.cs index 036d1c921..1c6dd96cb 100644 --- a/Dalamud/Game/Text/Evaluator/SeStringParameter.cs +++ b/Dalamud/Game/Text/Evaluator/SeStringParameter.cs @@ -3,6 +3,7 @@ using System.Globalization; using Lumina.Text.ReadOnly; using DSeString = Dalamud.Game.Text.SeStringHandling.SeString; +using LSeString = Lumina.Text.SeString; namespace Dalamud.Game.Text.Evaluator; @@ -70,6 +71,9 @@ public readonly struct SeStringParameter public static implicit operator SeStringParameter(ReadOnlySeStringSpan value) => new(new ReadOnlySeString(value)); + [Obsolete("Switch to using ReadOnlySeString instead of Lumina's SeString.", true)] + public static implicit operator SeStringParameter(LSeString value) => new(new ReadOnlySeString(value.RawData)); + public static implicit operator SeStringParameter(DSeString value) => new(new ReadOnlySeString(value.Encode())); public static implicit operator SeStringParameter(string value) => new(value); diff --git a/Dalamud/Game/Text/Noun/NounParams.cs b/Dalamud/Game/Text/Noun/NounParams.cs index ab7a732d2..3d5c424be 100644 --- a/Dalamud/Game/Text/Noun/NounParams.cs +++ b/Dalamud/Game/Text/Noun/NounParams.cs @@ -60,8 +60,8 @@ internal record struct NounParams() /// public readonly int ColumnOffset => this.SheetName switch { - // See "E8 ?? ?? ?? ?? 44 8B 66 ?? 8B E8" - nameof(LSheets.BeastTribe) => 11, + // See "E8 ?? ?? ?? ?? 44 8B 6B 08" + nameof(LSheets.BeastTribe) => 10, nameof(LSheets.DeepDungeonItem) => 1, nameof(LSheets.DeepDungeonEquipment) => 1, nameof(LSheets.DeepDungeonMagicStone) => 1, diff --git a/Dalamud/Game/Text/Noun/NounProcessor.cs b/Dalamud/Game/Text/Noun/NounProcessor.cs index c218bb26c..18f8cd4a9 100644 --- a/Dalamud/Game/Text/Noun/NounProcessor.cs +++ b/Dalamud/Game/Text/Noun/NounProcessor.cs @@ -9,6 +9,7 @@ using Dalamud.Utility; using Lumina.Excel; using Lumina.Text.ReadOnly; +using LSeStringBuilder = Lumina.Text.SeStringBuilder; using LSheets = Lumina.Excel.Sheets; namespace Dalamud.Game.Text.Noun; @@ -85,7 +86,7 @@ internal class NounProcessor : IServiceType private const int PronounColumnIdx = 6; private const int ArticleColumnIdx = 7; - private static readonly ModuleLog Log = ModuleLog.Create(); + private static readonly ModuleLog Log = new("NounProcessor"); [ServiceManager.ServiceDependency] private readonly DataManager dataManager = Service.Get(); @@ -146,28 +147,30 @@ internal class NounProcessor : IServiceType var attributiveSheet = this.dataManager.Excel.GetSheet(nounParams.Language.ToLumina(), nameof(LSheets.Attributive)); - using var rssb = new RentedSeStringBuilder(); + var builder = LSeStringBuilder.SharedPool.Get(); // Ko-So-A-Do var ksad = attributiveSheet.GetRow((uint)nounParams.ArticleType).ReadStringColumn(nounParams.Quantity > 1 ? 1 : 0); if (!ksad.IsEmpty) { - rssb.Builder.Append(ksad); + builder.Append(ksad); if (nounParams.Quantity > 1) { - rssb.Builder.ReplaceText("[n]"u8, ReadOnlySeString.FromText(nounParams.Quantity.ToString())); + builder.ReplaceText("[n]"u8, ReadOnlySeString.FromText(nounParams.Quantity.ToString())); } } if (!nounParams.LinkMarker.IsEmpty) - rssb.Builder.Append(nounParams.LinkMarker); + builder.Append(nounParams.LinkMarker); var text = row.ReadStringColumn(nounParams.ColumnOffset); if (!text.IsEmpty) - rssb.Builder.Append(text); + builder.Append(text); - return rssb.Builder.ToReadOnlySeString(); + var ross = builder.ToReadOnlySeString(); + LSeStringBuilder.SharedPool.Return(builder); + return ross; } /// @@ -197,7 +200,7 @@ internal class NounProcessor : IServiceType var attributiveSheet = this.dataManager.Excel.GetSheet(nounParams.Language.ToLumina(), nameof(LSheets.Attributive)); - using var rssb = new RentedSeStringBuilder(); + var builder = LSeStringBuilder.SharedPool.Get(); var isProperNounColumn = nounParams.ColumnOffset + ArticleColumnIdx; var isProperNoun = isProperNounColumn >= 0 ? row.ReadInt8Column(isProperNounColumn) : ~isProperNounColumn; @@ -213,19 +216,21 @@ internal class NounProcessor : IServiceType var article = attributiveSheet.GetRow((uint)nounParams.ArticleType) .ReadStringColumn(articleColumn + grammaticalNumberColumnOffset); if (!article.IsEmpty) - rssb.Builder.Append(article); + builder.Append(article); if (!nounParams.LinkMarker.IsEmpty) - rssb.Builder.Append(nounParams.LinkMarker); + builder.Append(nounParams.LinkMarker); } var text = row.ReadStringColumn(nounParams.ColumnOffset + (nounParams.Quantity == 1 ? SingularColumnIdx : PluralColumnIdx)); if (!text.IsEmpty) - rssb.Builder.Append(text); + builder.Append(text); - rssb.Builder.ReplaceText("[n]"u8, ReadOnlySeString.FromText(nounParams.Quantity.ToString())); + builder.ReplaceText("[n]"u8, ReadOnlySeString.FromText(nounParams.Quantity.ToString())); - return rssb.Builder.ToReadOnlySeString(); + var ross = builder.ToReadOnlySeString(); + LSeStringBuilder.SharedPool.Return(builder); + return ross; } /// @@ -257,13 +262,17 @@ internal class NounProcessor : IServiceType var attributiveSheet = this.dataManager.Excel.GetSheet(nounParams.Language.ToLumina(), nameof(LSheets.Attributive)); - using var rssb = new RentedSeStringBuilder(); + var builder = LSeStringBuilder.SharedPool.Get(); + ReadOnlySeString ross; if (nounParams.IsActionSheet) { - rssb.Builder.Append(row.ReadStringColumn(nounParams.GrammaticalCase)); - rssb.Builder.ReplaceText("[n]"u8, ReadOnlySeString.FromText(nounParams.Quantity.ToString())); - return rssb.Builder.ToReadOnlySeString(); + builder.Append(row.ReadStringColumn(nounParams.GrammaticalCase)); + builder.ReplaceText("[n]"u8, ReadOnlySeString.FromText(nounParams.Quantity.ToString())); + + ross = builder.ToReadOnlySeString(); + LSeStringBuilder.SharedPool.Return(builder); + return ross; } var genderIndexColumn = nounParams.ColumnOffset + PronounColumnIdx; @@ -293,32 +302,35 @@ internal class NounProcessor : IServiceType var grammaticalGender = attributiveSheet.GetRow((uint)nounParams.ArticleType) .ReadStringColumn(caseColumnOffset + genderIndex); // Genus if (!grammaticalGender.IsEmpty) - rssb.Builder.Append(grammaticalGender); + builder.Append(grammaticalGender); } if (!nounParams.LinkMarker.IsEmpty) - rssb.Builder.Append(nounParams.LinkMarker); + builder.Append(nounParams.LinkMarker); - rssb.Builder.Append(text); + builder.Append(text); var plural = attributiveSheet.GetRow((uint)(caseRowOffset + 26)) .ReadStringColumn(caseColumnOffset + genderIndex); - if (rssb.Builder.ContainsText("[p]"u8)) - rssb.Builder.ReplaceText("[p]"u8, plural); + if (builder.ContainsText("[p]"u8)) + builder.ReplaceText("[p]"u8, plural); else - rssb.Builder.Append(plural); + builder.Append(plural); if (hasT) { var article = attributiveSheet.GetRow(39).ReadStringColumn(caseColumnOffset + genderIndex); // Definiter Artikel - rssb.Builder.ReplaceText("[t]"u8, article); + builder.ReplaceText("[t]"u8, article); } } - rssb.Builder.ReplaceText("[pa]"u8, attributiveSheet.GetRow(24).ReadStringColumn(caseColumnOffset + genderIndex)); + var pa = attributiveSheet.GetRow(24).ReadStringColumn(caseColumnOffset + genderIndex); + builder.ReplaceText("[pa]"u8, pa); - var declensionRow = (GermanArticleType)nounParams.ArticleType switch + RawRow declensionRow; + + declensionRow = (GermanArticleType)nounParams.ArticleType switch { // Schwache Flexion eines Adjektivs?! GermanArticleType.Possessive or GermanArticleType.Demonstrative => attributiveSheet.GetRow(25), @@ -335,10 +347,14 @@ internal class NounProcessor : IServiceType _ => attributiveSheet.GetRow(26), }; - rssb.Builder.ReplaceText("[a]"u8, declensionRow.ReadStringColumn(caseColumnOffset + genderIndex)); - rssb.Builder.ReplaceText("[n]"u8, ReadOnlySeString.FromText(nounParams.Quantity.ToString())); + var declension = declensionRow.ReadStringColumn(caseColumnOffset + genderIndex); + builder.ReplaceText("[a]"u8, declension); - return rssb.Builder.ToReadOnlySeString(); + builder.ReplaceText("[n]"u8, ReadOnlySeString.FromText(nounParams.Quantity.ToString())); + + ross = builder.ToReadOnlySeString(); + LSeStringBuilder.SharedPool.Return(builder); + return ross; } /// @@ -369,7 +385,8 @@ internal class NounProcessor : IServiceType var attributiveSheet = this.dataManager.Excel.GetSheet(nounParams.Language.ToLumina(), nameof(LSheets.Attributive)); - using var rssb = new RentedSeStringBuilder(); + var builder = LSeStringBuilder.SharedPool.Get(); + ReadOnlySeString ross; var startsWithVowelColumn = nounParams.ColumnOffset + StartsWithVowelColumnIdx; var startsWithVowel = startsWithVowelColumn >= 0 @@ -388,19 +405,21 @@ internal class NounProcessor : IServiceType { var v21 = attributiveSheet.GetRow((uint)nounParams.ArticleType).ReadStringColumn(v20); if (!v21.IsEmpty) - rssb.Builder.Append(v21); + builder.Append(v21); if (!nounParams.LinkMarker.IsEmpty) - rssb.Builder.Append(nounParams.LinkMarker); + builder.Append(nounParams.LinkMarker); var text = row.ReadStringColumn(nounParams.ColumnOffset + (nounParams.Quantity <= 1 ? SingularColumnIdx : PluralColumnIdx)); if (!text.IsEmpty) - rssb.Builder.Append(text); + builder.Append(text); if (nounParams.Quantity <= 1) - rssb.Builder.ReplaceText("[n]"u8, ReadOnlySeString.FromText(nounParams.Quantity.ToString())); + builder.ReplaceText("[n]"u8, ReadOnlySeString.FromText(nounParams.Quantity.ToString())); - return rssb.Builder.ToReadOnlySeString(); + ross = builder.ToReadOnlySeString(); + LSeStringBuilder.SharedPool.Return(builder); + return ross; } var v17 = row.ReadInt8Column(nounParams.ColumnOffset + Unknown5ColumnIdx); @@ -409,32 +428,34 @@ internal class NounProcessor : IServiceType var v29 = attributiveSheet.GetRow((uint)nounParams.ArticleType).ReadStringColumn(v20 + 2); if (!v29.IsEmpty) { - rssb.Builder.Append(v29); + builder.Append(v29); if (!nounParams.LinkMarker.IsEmpty) - rssb.Builder.Append(nounParams.LinkMarker); + builder.Append(nounParams.LinkMarker); var text = row.ReadStringColumn(nounParams.ColumnOffset + PluralColumnIdx); if (!text.IsEmpty) - rssb.Builder.Append(text); + builder.Append(text); } } else { var v27 = attributiveSheet.GetRow((uint)nounParams.ArticleType).ReadStringColumn(v20 + (v17 != 0 ? 1 : 3)); if (!v27.IsEmpty) - rssb.Builder.Append(v27); + builder.Append(v27); if (!nounParams.LinkMarker.IsEmpty) - rssb.Builder.Append(nounParams.LinkMarker); + builder.Append(nounParams.LinkMarker); var text = row.ReadStringColumn(nounParams.ColumnOffset + SingularColumnIdx); if (!text.IsEmpty) - rssb.Builder.Append(text); + builder.Append(text); } - rssb.Builder.ReplaceText("[n]"u8, ReadOnlySeString.FromText(nounParams.Quantity.ToString())); + builder.ReplaceText("[n]"u8, ReadOnlySeString.FromText(nounParams.Quantity.ToString())); - return rssb.Builder.ToReadOnlySeString(); + ross = builder.ToReadOnlySeString(); + LSeStringBuilder.SharedPool.Return(builder); + return ross; } } diff --git a/Dalamud/Game/Text/SeStringHandling/Payload.cs b/Dalamud/Game/Text/SeStringHandling/Payload.cs index 685ff517a..c797c4a91 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payload.cs @@ -213,10 +213,11 @@ public abstract partial class Payload return payload; } - private static TextPayload DecodeText(BinaryReader reader) + private static Payload DecodeText(BinaryReader reader) { var payload = new TextPayload(); payload.DecodeImpl(reader, reader.BaseStream.Length); + return payload; } } @@ -381,7 +382,7 @@ public abstract partial class Payload { if (value < 0xCF) { - return [(byte)(value + 1)]; + return new byte[] { (byte)(value + 1) }; } var bytes = BitConverter.GetBytes(value); diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/AutoTranslatePayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/AutoTranslatePayload.cs index 8178a6d33..470e942c3 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/AutoTranslatePayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/AutoTranslatePayload.cs @@ -1,7 +1,6 @@ using System.IO; using Dalamud.Game.Text.Evaluator; -using Dalamud.Utility; using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; @@ -33,14 +32,13 @@ public class AutoTranslatePayload : Payload, ITextProvider this.Group = group; this.Key = key; - using var rssb = new RentedSeStringBuilder(); - - this.payload = rssb.Builder - .BeginMacro(MacroCode.Fixed) - .AppendUIntExpression(group - 1) - .AppendUIntExpression(key) - .EndMacro() - .ToReadOnlySeString(); + var ssb = Lumina.Text.SeStringBuilder.SharedPool.Get(); + this.payload = ssb.BeginMacro(MacroCode.Fixed) + .AppendUIntExpression(group - 1) + .AppendUIntExpression(key) + .EndMacro() + .ToReadOnlySeString(); + Lumina.Text.SeStringBuilder.SharedPool.Return(ssb); } /// diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/DalamudLinkPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/DalamudLinkPayload.cs index 2becb815b..8b020b111 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/DalamudLinkPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/DalamudLinkPayload.cs @@ -1,7 +1,5 @@ using System.IO; -using Dalamud.Utility; - using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; @@ -39,18 +37,19 @@ public class DalamudLinkPayload : Payload /// protected override byte[] EncodeImpl() { - using var rssb = new RentedSeStringBuilder(); - return rssb.Builder - .BeginMacro(MacroCode.Link) - .AppendIntExpression((int)EmbeddedInfoType.DalamudLink - 1) - .AppendUIntExpression(this.CommandId) - .AppendIntExpression(this.Extra1) - .AppendIntExpression(this.Extra2) - .BeginStringExpression() - .Append(JsonConvert.SerializeObject(new[] { this.Plugin, this.ExtraString })) - .EndExpression() - .EndMacro() - .ToArray(); + var ssb = Lumina.Text.SeStringBuilder.SharedPool.Get(); + var res = ssb.BeginMacro(MacroCode.Link) + .AppendIntExpression((int)EmbeddedInfoType.DalamudLink - 1) + .AppendUIntExpression(this.CommandId) + .AppendIntExpression(this.Extra1) + .AppendIntExpression(this.Extra2) + .BeginStringExpression() + .Append(JsonConvert.SerializeObject(new[] { this.Plugin, this.ExtraString })) + .EndExpression() + .EndMacro() + .ToArray(); + Lumina.Text.SeStringBuilder.SharedPool.Return(ssb); + return res; } /// diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/IconPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/IconPayload.cs index ad25de01d..7963d422f 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/IconPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/IconPayload.cs @@ -45,10 +45,10 @@ public class IconPayload : Payload { var indexBytes = MakeInteger((uint)this.Icon); var chunkLen = indexBytes.Length + 1; - var bytes = new List( - [ + var bytes = new List(new byte[] + { START_BYTE, (byte)SeStringChunkType.Icon, (byte)chunkLen, - ]); + }); bytes.AddRange(indexBytes); bytes.Add(END_BYTE); return bytes.ToArray(); diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/ItemPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/ItemPayload.cs index 20f05958f..0c1f75a1d 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/ItemPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/ItemPayload.cs @@ -8,7 +8,6 @@ using Dalamud.Utility; using Lumina.Excel; using Lumina.Excel.Sheets; - using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling.Payloads; @@ -173,7 +172,7 @@ public class ItemPayload : Payload }; bytes.AddRange(idBytes); // unk - bytes.AddRange([0x02, 0x01]); + bytes.AddRange(new byte[] { 0x02, 0x01 }); // Links don't have to include the name, but if they do, it requires additional work if (hasName) @@ -184,17 +183,17 @@ public class ItemPayload : Payload nameLen += 4; // space plus 3 bytes for HQ symbol } - bytes.AddRange( - [ + bytes.AddRange(new byte[] + { 0xFF, // unk (byte)nameLen, - ]); + }); bytes.AddRange(Encoding.UTF8.GetBytes(this.displayName)); if (this.IsHQ) { // space and HQ symbol - bytes.AddRange([0x20, 0xEE, 0x80, 0xBC]); + bytes.AddRange(new byte[] { 0x20, 0xEE, 0x80, 0xBC }); } } diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/MapLinkPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/MapLinkPayload.cs index 31cab41a1..7b672d07a 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/MapLinkPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/MapLinkPayload.cs @@ -5,7 +5,6 @@ using Dalamud.Data; using Lumina.Excel; using Lumina.Excel.Sheets; - using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling.Payloads; @@ -175,7 +174,7 @@ public class MapLinkPayload : Payload bytes.AddRange(yBytes); // unk - bytes.AddRange([0xFF, 0x01, END_BYTE]); + bytes.AddRange(new byte[] { 0xFF, 0x01, END_BYTE }); return bytes.ToArray(); } diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/NewLinePayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/NewLinePayload.cs index 8072c87f5..0b090b3d6 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/NewLinePayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/NewLinePayload.cs @@ -7,7 +7,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads; /// public class NewLinePayload : Payload, ITextProvider { - private readonly byte[] bytes = [START_BYTE, (byte)SeStringChunkType.NewLine, 0x01, END_BYTE]; + private readonly byte[] bytes = { START_BYTE, (byte)SeStringChunkType.NewLine, 0x01, END_BYTE }; /// /// Gets an instance of NewLinePayload. diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/PartyFinderPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/PartyFinderPayload.cs index 77ed7b8bc..0931ab03f 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/PartyFinderPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/PartyFinderPayload.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using Lumina.Extensions; - using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling.Payloads @@ -98,7 +97,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads reader.ReadByte(); // if the next byte is 0xF3 then this listing is limited to home world - var nextByte = reader.ReadByte(); + byte nextByte = reader.ReadByte(); switch (nextByte) { case (byte)PartyFinderLinkType.LimitedToHomeWorld: @@ -122,11 +121,11 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads // if the link type is notification, just use premade payload data since it's always the same. // i have no idea why it is formatted like this, but it is how it is. // note it is identical to the link terminator payload except the embedded info type is 0x08 - if (this.LinkType == PartyFinderLinkType.PartyFinderNotification) return [0x02, 0x27, 0x07, 0x08, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03,]; + if (this.LinkType == PartyFinderLinkType.PartyFinderNotification) return new byte[] { 0x02, 0x27, 0x07, 0x08, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03, }; // back to our regularly scheduled programming... var listingIDBytes = MakeInteger(this.ListingId); - var isFlagSpecified = this.LinkType != PartyFinderLinkType.NotSpecified; + bool isFlagSpecified = this.LinkType != PartyFinderLinkType.NotSpecified; var chunkLen = listingIDBytes.Length + 4; // 1 more byte for the type flag if it is specified diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/PlayerPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/PlayerPayload.cs index 01ca1b955..55697782e 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/PlayerPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/PlayerPayload.cs @@ -1,7 +1,8 @@ +using System.Collections.Generic; using System.IO; +using System.Text; using Dalamud.Data; -using Dalamud.Utility; using Lumina.Excel; using Lumina.Excel.Sheets; @@ -86,12 +87,14 @@ public class PlayerPayload : Payload /// protected override byte[] EncodeImpl() { - using var rssb = new RentedSeStringBuilder(); - return rssb.Builder - .PushLinkCharacter(this.playerName, this.serverId) - .Append(this.playerName) - .PopLink() - .ToArray(); + var ssb = Lumina.Text.SeStringBuilder.SharedPool.Get(); + var res = ssb + .PushLinkCharacter(this.playerName, this.serverId) + .Append(this.playerName) + .PopLink() + .ToArray(); + Lumina.Text.SeStringBuilder.SharedPool.Return(ssb); + return res; } /// diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/QuestPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/QuestPayload.cs index 47947ccde..19d494d8a 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/QuestPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/QuestPayload.cs @@ -5,7 +5,6 @@ using Dalamud.Data; using Lumina.Excel; using Lumina.Excel.Sheets; - using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling.Payloads; @@ -63,7 +62,7 @@ public class QuestPayload : Payload }; bytes.AddRange(idBytes); - bytes.AddRange([0x01, 0x01, END_BYTE]); + bytes.AddRange(new byte[] { 0x01, 0x01, END_BYTE }); return bytes.ToArray(); } diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/RawPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/RawPayload.cs index 50464077b..02a7c113e 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/RawPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/RawPayload.cs @@ -45,7 +45,7 @@ public class RawPayload : Payload /// /// Gets a fixed Payload representing a common link-termination sequence, found in many payload chains. /// - public static RawPayload LinkTerminator => new([0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03]); + public static RawPayload LinkTerminator => new(new byte[] { 0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03 }); /// public override PayloadType Type => PayloadType.Unknown; diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/SeHyphenPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/SeHyphenPayload.cs index 48c55a5a8..1739b9cda 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/SeHyphenPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/SeHyphenPayload.cs @@ -7,7 +7,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads; /// public class SeHyphenPayload : Payload, ITextProvider { - private readonly byte[] bytes = [START_BYTE, (byte)SeStringChunkType.SeHyphen, 0x01, END_BYTE]; + private readonly byte[] bytes = { START_BYTE, (byte)SeStringChunkType.SeHyphen, 0x01, END_BYTE }; /// /// Gets an instance of SeHyphenPayload. diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/StatusPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/StatusPayload.cs index c17213f60..d102dfab6 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/StatusPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/StatusPayload.cs @@ -5,7 +5,6 @@ using Dalamud.Data; using Lumina.Excel; using Lumina.Excel.Sheets; - using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling.Payloads; @@ -64,7 +63,7 @@ public class StatusPayload : Payload bytes.AddRange(idBytes); // unk - bytes.AddRange([0x01, 0x01, 0xFF, 0x02, 0x20, END_BYTE]); + bytes.AddRange(new byte[] { 0x01, 0x01, 0xFF, 0x02, 0x20, END_BYTE }); return bytes.ToArray(); } diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/UIForegroundPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/UIForegroundPayload.cs index f161cff9d..bf360ce34 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/UIForegroundPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/UIForegroundPayload.cs @@ -5,7 +5,6 @@ using Dalamud.Data; using Lumina.Excel; using Lumina.Excel.Sheets; - using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling.Payloads; @@ -96,10 +95,10 @@ public class UIForegroundPayload : Payload var colorBytes = MakeInteger(this.colorKey); var chunkLen = colorBytes.Length + 1; - var bytes = new List( - [ + var bytes = new List(new byte[] + { START_BYTE, (byte)SeStringChunkType.UIForeground, (byte)chunkLen, - ]); + }); bytes.AddRange(colorBytes); bytes.Add(END_BYTE); diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/UIGlowPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/UIGlowPayload.cs index 01c0d05d2..e54427073 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/UIGlowPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/UIGlowPayload.cs @@ -5,7 +5,6 @@ using Dalamud.Data; using Lumina.Excel; using Lumina.Excel.Sheets; - using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling.Payloads; @@ -99,10 +98,10 @@ public class UIGlowPayload : Payload var colorBytes = MakeInteger(this.colorKey); var chunkLen = colorBytes.Length + 1; - var bytes = new List( - [ + var bytes = new List(new byte[] + { START_BYTE, (byte)SeStringChunkType.UIGlow, (byte)chunkLen, - ]); + }); bytes.AddRange(colorBytes); bytes.Add(END_BYTE); diff --git a/Dalamud/Game/Text/SeStringHandling/SeString.cs b/Dalamud/Game/Text/SeStringHandling/SeString.cs index b64a8bb67..8805c2177 100644 --- a/Dalamud/Game/Text/SeStringHandling/SeString.cs +++ b/Dalamud/Game/Text/SeStringHandling/SeString.cs @@ -28,7 +28,7 @@ public class SeString /// public SeString() { - this.Payloads = []; + this.Payloads = new List(); } /// @@ -113,6 +113,14 @@ public class SeString /// Equivalent SeString. public static implicit operator SeString(string str) => new(new TextPayload(str)); + /// + /// Implicitly convert a string into a SeString containing a . + /// + /// string to convert. + /// Equivalent SeString. + [Obsolete("Switch to using ReadOnlySeString instead of Lumina's SeString.", true)] + public static explicit operator SeString(Lumina.Text.SeString str) => str.ToDalamudString(); + /// /// Parse a binary game message into an SeString. /// @@ -198,9 +206,8 @@ public class SeString var textColor = ItemUtil.GetItemRarityColorType(rawId); var textEdgeColor = textColor + 1u; - using var rssb = new RentedSeStringBuilder(); - - var itemLink = rssb.Builder + var sb = LSeStringBuilder.SharedPool.Get(); + var itemLink = sb .PushColorType(textColor) .PushEdgeColorType(textEdgeColor) .PushLinkItem(rawId, copyName) @@ -209,6 +216,7 @@ public class SeString .PopEdgeColorType() .PopColorType() .ToReadOnlySeString(); + LSeStringBuilder.SharedPool.Return(sb); return SeString.Parse(seStringEvaluator.EvaluateFromAddon(371, [itemLink], clientState.ClientLanguage)); } @@ -250,12 +258,16 @@ public class SeString var mapPayload = new MapLinkPayload(territoryId, mapId, rawX, rawY); var nameString = GetMapLinkNameString(mapPayload.PlaceName, instance, mapPayload.CoordinateString); - return new SeString(new List([ + var payloads = new List(new Payload[] + { mapPayload, - ..TextArrowPayloads, + // arrow goes here new TextPayload(nameString), RawPayload.LinkTerminator, - ])); + }); + payloads.InsertRange(1, TextArrowPayloads); + + return new SeString(payloads); } /// @@ -286,12 +298,16 @@ public class SeString var mapPayload = new MapLinkPayload(territoryId, mapId, xCoord, yCoord, fudgeFactor); var nameString = GetMapLinkNameString(mapPayload.PlaceName, instance, mapPayload.CoordinateString); - return new SeString(new List([ + var payloads = new List(new Payload[] + { mapPayload, - ..TextArrowPayloads, + // arrow goes here new TextPayload(nameString), RawPayload.LinkTerminator, - ])); + }); + payloads.InsertRange(1, TextArrowPayloads); + + return new SeString(payloads); } /// @@ -347,15 +363,21 @@ public class SeString /// An SeString containing all the payloads necessary to display a party finder link in the chat log. public static SeString CreatePartyFinderLink(uint listingId, string recruiterName, bool isCrossWorld = false) { - var clientState = Service.Get(); - var seStringEvaluator = Service.Get(); - - return new SeString(new List([ + var payloads = new List() + { new PartyFinderPayload(listingId, isCrossWorld ? PartyFinderPayload.PartyFinderLinkType.NotSpecified : PartyFinderPayload.PartyFinderLinkType.LimitedToHomeWorld), - ..TextArrowPayloads, - ..SeString.Parse(seStringEvaluator.EvaluateFromAddon(2265, [recruiterName, isCrossWorld ? 0 : 1], clientState.ClientLanguage)).Payloads, - RawPayload.LinkTerminator - ])); + // -> + new TextPayload($"Looking for Party ({recruiterName})" + (isCrossWorld ? " " : string.Empty)), + }; + + payloads.InsertRange(1, TextArrowPayloads); + + if (isCrossWorld) + payloads.Add(new IconPayload(BitmapFontIcon.CrossWorld)); + + payloads.Add(RawPayload.LinkTerminator); + + return new SeString(payloads); } /// @@ -365,12 +387,16 @@ public class SeString /// An SeString containing all the payloads necessary to display a link to the party finder search conditions. public static SeString CreatePartyFinderSearchConditionsLink(string message) { - return new SeString(new List([ + var payloads = new List() + { new PartyFinderPayload(), - ..TextArrowPayloads, + // -> new TextPayload(message), - RawPayload.LinkTerminator - ])); + }; + payloads.InsertRange(1, TextArrowPayloads); + payloads.Add(RawPayload.LinkTerminator); + + return new SeString(payloads); } /// diff --git a/Dalamud/Game/UnlockState/ItemActionAction.cs b/Dalamud/Game/UnlockState/ItemActionType.cs similarity index 96% rename from Dalamud/Game/UnlockState/ItemActionAction.cs rename to Dalamud/Game/UnlockState/ItemActionType.cs index 0e86fcb67..8e3d79b84 100644 --- a/Dalamud/Game/UnlockState/ItemActionAction.cs +++ b/Dalamud/Game/UnlockState/ItemActionType.cs @@ -3,9 +3,9 @@ using Lumina.Excel.Sheets; namespace Dalamud.Game.UnlockState; /// -/// Enum for . +/// Enum for . /// -internal enum ItemActionAction : ushort +internal enum ItemActionType : ushort { /// /// No item action. diff --git a/Dalamud/Game/UnlockState/RecipeData.cs b/Dalamud/Game/UnlockState/RecipeData.cs index 7fa0d4b8f..c419ba4fd 100644 --- a/Dalamud/Game/UnlockState/RecipeData.cs +++ b/Dalamud/Game/UnlockState/RecipeData.cs @@ -158,23 +158,67 @@ internal unsafe class RecipeData : IInternalDisposableService { noteBookDivisionIndex++; - if (!noteBookDivisionRow.AllowedCraftTypes[craftType]) - continue; + // For future Lumina.Excel update, replace with: + // if (!notebookDivisionRow.AllowedCraftTypes[craftType]) + // continue; + + switch (craftTypeRow.RowId) + { + case 0 when !noteBookDivisionRow.CRPCraft: continue; + case 1 when !noteBookDivisionRow.BSMCraft: continue; + case 2 when !noteBookDivisionRow.ARMCraft: continue; + case 3 when !noteBookDivisionRow.GSMCraft: continue; + case 4 when !noteBookDivisionRow.LTWCraft: continue; + case 5 when !noteBookDivisionRow.WVRCraft: continue; + case 6 when !noteBookDivisionRow.ALCCraft: continue; + case 7 when !noteBookDivisionRow.CULCraft: continue; + } if (noteBookDivisionRow.GatheringOpeningLevel != byte.MaxValue) continue; - if (noteBookDivisionRow.RequiresSecretRecipeBookGroupUnlock) + // For future Lumina.Excel update, replace with: + // if (notebookDivisionRow.RequiresSecretRecipeBookGroupUnlock) + if (noteBookDivisionRow.Unknown1) { var secretRecipeBookUnlocked = false; - foreach (var secretRecipeBookGroup in noteBookDivisionRow.SecretRecipeBookGroups) + // For future Lumina.Excel update, iterate over notebookDivisionRow.SecretRecipeBookGroups + for (var i = 0; i < 2; i++) { - if (secretRecipeBookGroup.RowId == 0 || !secretRecipeBookGroup.IsValid) + // For future Lumina.Excel update, replace with: + // if (secretRecipeBookGroup.RowId == 0 || !secretRecipeBookGroup.IsValid) + // continue; + var secretRecipeBookGroupRowId = i switch + { + 0 => noteBookDivisionRow.Unknown2, + 1 => noteBookDivisionRow.Unknown2, + _ => default, + }; + + if (secretRecipeBookGroupRowId == 0) continue; - var bitIndex = secretRecipeBookGroup.Value.SecretRecipeBook[craftType].RowId; - if (PlayerState.Instance()->UnlockedSecretRecipeBooksBitArray.Get((int)bitIndex)) + if (!this.dataManager.GetExcelSheet().TryGetRow(secretRecipeBookGroupRowId, out var secretRecipeBookGroupRow)) + continue; + + // For future Lumina.Excel update, replace with: + // var bitIndex = secretRecipeBookGroup.Value.UnlockBitIndex[craftType]; + + var bitIndex = craftType switch + { + 0 => secretRecipeBookGroupRow.Unknown0, + 1 => secretRecipeBookGroupRow.Unknown1, + 2 => secretRecipeBookGroupRow.Unknown2, + 3 => secretRecipeBookGroupRow.Unknown3, + 4 => secretRecipeBookGroupRow.Unknown4, + 5 => secretRecipeBookGroupRow.Unknown5, + 6 => secretRecipeBookGroupRow.Unknown6, + 7 => secretRecipeBookGroupRow.Unknown7, + _ => default, + }; + + if (PlayerState.Instance()->UnlockedSecretRecipeBooksBitArray.Get(bitIndex)) { secretRecipeBookUnlocked = true; break; diff --git a/Dalamud/Game/UnlockState/UnlockState.cs b/Dalamud/Game/UnlockState/UnlockState.cs index 4b83e114a..cd896ffb6 100644 --- a/Dalamud/Game/UnlockState/UnlockState.cs +++ b/Dalamud/Game/UnlockState/UnlockState.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using Dalamud.Data; using Dalamud.Game.Gui; -using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Logging.Internal; @@ -17,14 +16,14 @@ using FFXIVClientStructs.FFXIV.Component.Exd; using Lumina.Excel; using Lumina.Excel.Sheets; -using AchievementSheet = Lumina.Excel.Sheets.Achievement; using ActionSheet = Lumina.Excel.Sheets.Action; -using CSAchievement = FFXIVClientStructs.FFXIV.Client.Game.UI.Achievement; using InstanceContentSheet = Lumina.Excel.Sheets.InstanceContent; using PublicContentSheet = Lumina.Excel.Sheets.PublicContent; namespace Dalamud.Game.UnlockState; +#pragma warning disable Dalamud001 + /// /// This class provides unlock state of various content in the game. /// @@ -33,6 +32,8 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState { private static readonly ModuleLog Log = new(nameof(UnlockState)); + private readonly ConcurrentDictionary> cachedUnlockedRowIds = []; + [ServiceManager.ServiceDependency] private readonly DataManager dataManager = Service.Get(); @@ -45,38 +46,17 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState [ServiceManager.ServiceDependency] private readonly RecipeData recipeData = Service.Get(); - private readonly ConcurrentDictionary> cachedUnlockedRowIds = []; - private readonly Hook setAchievementCompletedHook; - private readonly Hook setTitleUnlockedHook; - [ServiceManager.ServiceConstructor] private UnlockState() { this.clientState.Login += this.OnLogin; this.clientState.Logout += this.OnLogout; this.gameGui.AgentUpdate += this.OnAgentUpdate; - - this.setAchievementCompletedHook = Hook.FromAddress( - (nint)CSAchievement.MemberFunctionPointers.SetAchievementCompleted, - this.SetAchievementCompletedDetour); - - this.setTitleUnlockedHook = Hook.FromAddress( - (nint)TitleList.MemberFunctionPointers.SetTitleUnlocked, - this.SetTitleUnlockedDetour); - - this.setAchievementCompletedHook.Enable(); - this.setTitleUnlockedHook.Enable(); } /// public event IUnlockState.UnlockDelegate Unlock; - /// - public bool IsAchievementListLoaded => CSAchievement.Instance()->IsLoaded(); - - /// - public bool IsTitleListLoaded => UIState.Instance()->TitleList.DataReceived; - private bool IsLoaded => PlayerState.Instance()->IsLoaded; /// @@ -85,21 +65,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState this.clientState.Login -= this.OnLogin; this.clientState.Logout -= this.OnLogout; this.gameGui.AgentUpdate -= this.OnAgentUpdate; - - this.setAchievementCompletedHook.Dispose(); - } - - /// - public bool IsAchievementComplete(AchievementSheet row) - { - // Only check for login state here as individual Achievements - // may be flagged as complete when you unlock them, regardless - // of whether the full Achievements list was loaded or not. - - if (!this.IsLoaded) - return false; - - return CSAchievement.Instance()->IsComplete((int)row.RowId); } /// @@ -108,15 +73,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState return this.IsUnlockLinkUnlocked(row.UnlockLink.RowId); } - /// - public bool IsAdventureComplete(Adventure row) - { - if (!this.IsLoaded) - return false; - - return PlayerState.Instance()->IsAdventureComplete(row.RowId - 0x210000); - } - /// public bool IsAetherCurrentUnlocked(AetherCurrent row) { @@ -253,7 +209,7 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState /// public bool IsEmjVoiceNpcUnlocked(EmjVoiceNpc row) { - return this.IsUnlockLinkUnlocked(row.UnlockLink); + return this.IsUnlockLinkUnlocked(row.Unknown26); } /// @@ -261,7 +217,7 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState { return this.dataManager.GetExcelSheet().TryGetRow(row.RowId, out var emjVoiceNpcRow) && this.IsEmjVoiceNpcUnlocked(emjVoiceNpcRow) - && QuestManager.IsQuestComplete(row.UnlockQuest.RowId); + && QuestManager.IsQuestComplete(row.Unknown1); } /// @@ -308,47 +264,47 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState // To avoid the ExdModule.GetItemRowById call, which can return null if the excel page // is not loaded, we're going to imitate the IsItemActionUnlocked call first: - switch ((ItemActionAction)row.ItemAction.Value.Action.RowId) + switch ((ItemActionType)row.ItemAction.Value.Type) { - case ItemActionAction.Companion: + case ItemActionType.Companion: return UIState.Instance()->IsCompanionUnlocked(row.ItemAction.Value.Data[0]); - case ItemActionAction.BuddyEquip: + case ItemActionType.BuddyEquip: return UIState.Instance()->Buddy.CompanionInfo.IsBuddyEquipUnlocked(row.ItemAction.Value.Data[0]); - case ItemActionAction.Mount: + case ItemActionType.Mount: return PlayerState.Instance()->IsMountUnlocked(row.ItemAction.Value.Data[0]); - case ItemActionAction.SecretRecipeBook: + case ItemActionType.SecretRecipeBook: return PlayerState.Instance()->IsSecretRecipeBookUnlocked(row.ItemAction.Value.Data[0]); - case ItemActionAction.UnlockLink: - case ItemActionAction.OccultRecords: + case ItemActionType.UnlockLink: + case ItemActionType.OccultRecords: return UIState.Instance()->IsUnlockLinkUnlocked(row.ItemAction.Value.Data[0]); - case ItemActionAction.TripleTriadCard when row.AdditionalData.Is(): + case ItemActionType.TripleTriadCard when row.AdditionalData.Is(): return UIState.Instance()->IsTripleTriadCardUnlocked((ushort)row.AdditionalData.RowId); - case ItemActionAction.FolkloreTome: + case ItemActionType.FolkloreTome: return PlayerState.Instance()->IsFolkloreBookUnlocked(row.ItemAction.Value.Data[0]); - case ItemActionAction.OrchestrionRoll when row.AdditionalData.Is(): + case ItemActionType.OrchestrionRoll when row.AdditionalData.Is(): return PlayerState.Instance()->IsOrchestrionRollUnlocked(row.AdditionalData.RowId); - case ItemActionAction.FramersKit: + case ItemActionType.FramersKit: return PlayerState.Instance()->IsFramersKitUnlocked(row.AdditionalData.RowId); - case ItemActionAction.Ornament: + case ItemActionType.Ornament: return PlayerState.Instance()->IsOrnamentUnlocked(row.ItemAction.Value.Data[0]); - case ItemActionAction.Glasses: + case ItemActionType.Glasses: return PlayerState.Instance()->IsGlassesUnlocked((ushort)row.AdditionalData.RowId); - case ItemActionAction.SoulShards when PublicContentOccultCrescent.GetState() is var occultCrescentState && occultCrescentState != null: + case ItemActionType.SoulShards when PublicContentOccultCrescent.GetState() is var occultCrescentState && occultCrescentState != null: var supportJobId = (byte)row.ItemAction.Value.Data[0]; return supportJobId < occultCrescentState->SupportJobLevels.Length && occultCrescentState->SupportJobLevels[supportJobId] != 0; - case ItemActionAction.CompanySealVouchers: + case ItemActionType.CompanySealVouchers: return false; } @@ -357,12 +313,9 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState } /// - public bool IsLeveCompleted(Leve row) + public bool IsMcGuffinUnlocked(McGuffin row) { - if (!this.IsLoaded) - return false; - - return QuestManager.Instance()->IsLevequestComplete((ushort)row.RowId); + return PlayerState.Instance()->IsMcGuffinUnlocked(row.RowId); } /// @@ -374,16 +327,7 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState /// public bool IsMKDLoreUnlocked(MKDLore row) { - return this.IsUnlockLinkUnlocked(row.UnlockLink); - } - - /// - public bool IsMcGuffinUnlocked(McGuffin row) - { - if (!this.IsLoaded) - return false; - - return PlayerState.Instance()->IsMcGuffinUnlocked(row.RowId); + return this.IsUnlockLinkUnlocked(row.Unknown2); } /// @@ -434,21 +378,9 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState return UIState.IsPublicContentUnlocked(row.RowId); } - /// - public bool IsQuestCompleted(Quest row) - { - if (!this.IsLoaded) - return false; - - return QuestManager.IsQuestComplete(row.RowId); - } - /// public bool IsRecipeUnlocked(Recipe row) { - if (!this.IsLoaded) - return false; - return this.recipeData.IsRecipeUnlocked(row); } @@ -461,19 +393,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState return PlayerState.Instance()->IsSecretRecipeBookUnlocked(row.RowId); } - /// - public bool IsTitleUnlocked(Title row) - { - // Only check for login state here as individual Titles - // may be flagged as complete when you unlock them, regardless - // of whether the full Titles list was loaded or not. - - if (!this.IsLoaded) - return false; - - return UIState.Instance()->TitleList.IsTitleUnlocked((ushort)row.RowId); - } - /// public bool IsTraitUnlocked(Trait row) { @@ -495,20 +414,20 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState if (row.ItemAction.RowId == 0) return false; - return (ItemActionAction)row.ItemAction.Value.Action.RowId - is ItemActionAction.Companion - or ItemActionAction.BuddyEquip - or ItemActionAction.Mount - or ItemActionAction.SecretRecipeBook - or ItemActionAction.UnlockLink - or ItemActionAction.TripleTriadCard - or ItemActionAction.FolkloreTome - or ItemActionAction.OrchestrionRoll - or ItemActionAction.FramersKit - or ItemActionAction.Ornament - or ItemActionAction.Glasses - or ItemActionAction.OccultRecords - or ItemActionAction.SoulShards; + return (ItemActionType)row.ItemAction.Value.Type + is ItemActionType.Companion + or ItemActionType.BuddyEquip + or ItemActionType.Mount + or ItemActionType.SecretRecipeBook + or ItemActionType.UnlockLink + or ItemActionType.TripleTriadCard + or ItemActionType.FolkloreTome + or ItemActionType.OrchestrionRoll + or ItemActionType.FramersKit + or ItemActionType.Ornament + or ItemActionType.Glasses + or ItemActionType.OccultRecords + or ItemActionType.SoulShards; } /// @@ -523,15 +442,9 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState if (!this.IsLoaded || rowRef.IsUntyped) return false; - if (rowRef.TryGetValue(out var achievementRow)) - return this.IsAchievementComplete(achievementRow); - if (rowRef.TryGetValue(out var actionRow)) return this.IsActionUnlocked(actionRow); - if (rowRef.TryGetValue(out var adventureRow)) - return this.IsAdventureComplete(adventureRow); - if (rowRef.TryGetValue(out var aetherCurrentRow)) return this.IsAetherCurrentUnlocked(aetherCurrentRow); @@ -598,9 +511,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState if (rowRef.TryGetValue(out var itemRow)) return this.IsItemUnlocked(itemRow); - if (rowRef.TryGetValue(out var leveRow)) - return this.IsLeveCompleted(leveRow); - if (rowRef.TryGetValue(out var mjiLandmarkRow)) return this.IsMJILandmarkUnlocked(mjiLandmarkRow); @@ -628,18 +538,12 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState if (rowRef.TryGetValue(out var publicContentRow)) return this.IsPublicContentUnlocked(publicContentRow); - if (rowRef.TryGetValue(out var questRow)) - return this.IsQuestCompleted(questRow); - if (rowRef.TryGetValue(out var recipeRow)) return this.IsRecipeUnlocked(recipeRow); if (rowRef.TryGetValue(out var secretRecipeBookRow)) return this.IsSecretRecipeBookUnlocked(secretRecipeBookRow); - if (rowRef.TryGetValue(out var titleRow)) - return this.IsTitleUnlocked(titleRow); - if (rowRef.TryGetValue<Trait>(out var traitRow)) return this.IsTraitUnlocked(traitRow); @@ -689,37 +593,12 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState this.Update(); } - private void SetAchievementCompletedDetour(CSAchievement* thisPtr, uint id) - { - this.setAchievementCompletedHook.Original(thisPtr, id); - - if (!this.IsLoaded) - return; - - this.RaiseUnlockSafely((RowRef)LuminaUtils.CreateRef<AchievementSheet>(id)); - } - - private void SetTitleUnlockedDetour(TitleList* thisPtr, ushort id) - { - this.setTitleUnlockedHook.Original(thisPtr, id); - - if (!this.IsLoaded) - return; - - this.RaiseUnlockSafely((RowRef)LuminaUtils.CreateRef<Title>(id)); - } - private void Update() { if (!this.IsLoaded) return; - Log.Verbose("Checking for new unlocks..."); - - // Do not check for Achievements or Titles here! - this.UpdateUnlocksForSheet<ActionSheet>(); - this.UpdateUnlocksForSheet<Adventure>(); this.UpdateUnlocksForSheet<AetherCurrent>(); this.UpdateUnlocksForSheet<AetherCurrentCompFlgSet>(); this.UpdateUnlocksForSheet<AozAction>(); @@ -752,7 +631,6 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState this.UpdateUnlocksForSheet<Ornament>(); this.UpdateUnlocksForSheet<Perform>(); this.UpdateUnlocksForSheet<PublicContentSheet>(); - this.UpdateUnlocksForSheet<Quest>(); this.UpdateUnlocksForSheet<Recipe>(); this.UpdateUnlocksForSheet<SecretRecipeBook>(); this.UpdateUnlocksForSheet<Trait>(); @@ -761,11 +639,11 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState // Not implemented: // - DescriptionPage: quite complex // - QuestAcceptAdditionCondition: ignored - // - Leve: AgentUpdateFlag.UnlocksUpdate is not set and the completed status can be unset again! // For some other day: // - FishingSpot // - Spearfishing + // - Adventure (Sightseeing) // - MinerFolkloreTome // - BotanistFolkloreTome // - FishingFolkloreTome @@ -778,6 +656,8 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState // - EmjCostume // Probably not happening, because it requires fetching data from server: + // - Achievements + // - Titles // - Bozjan Field Notes // - Support/Phantom Jobs, which require to be in Occult Crescent, because it checks the jobs level for != 0 } @@ -798,23 +678,18 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState unlockedRowIds.Add(row.RowId); - // Log.Verbose($"Unlock detected: {typeof(T).Name}#{row.RowId}"); + Log.Verbose($"Unlock detected: {typeof(T).Name}#{row.RowId}"); - this.RaiseUnlockSafely((RowRef)rowRef); - } - } - - private void RaiseUnlockSafely(RowRef rowRef) - { - foreach (var action in Delegate.EnumerateInvocationList(this.Unlock)) - { - try + foreach (var action in Delegate.EnumerateInvocationList(this.Unlock)) { - action(rowRef); - } - catch (Exception ex) - { - Log.Error(ex, "Exception during raise of {handler}", action.Method); + try + { + action((RowRef)rowRef); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); + } } } } @@ -844,21 +719,9 @@ internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockStat /// <inheritdoc/> public event IUnlockState.UnlockDelegate? Unlock; - /// <inheritdoc/> - public bool IsAchievementListLoaded => this.unlockStateService.IsAchievementListLoaded; - - /// <inheritdoc/> - public bool IsTitleListLoaded => this.unlockStateService.IsTitleListLoaded; - - /// <inheritdoc/> - public bool IsAchievementComplete(AchievementSheet row) => this.unlockStateService.IsAchievementComplete(row); - /// <inheritdoc/> public bool IsActionUnlocked(ActionSheet row) => this.unlockStateService.IsActionUnlocked(row); - /// <inheritdoc/> - public bool IsAdventureComplete(Adventure row) => this.unlockStateService.IsAdventureComplete(row); - /// <inheritdoc/> public bool IsAetherCurrentCompFlgSetUnlocked(AetherCurrentCompFlgSet row) => this.unlockStateService.IsAetherCurrentCompFlgSetUnlocked(row); @@ -935,7 +798,7 @@ internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockStat public bool IsItemUnlocked(Item row) => this.unlockStateService.IsItemUnlocked(row); /// <inheritdoc/> - public bool IsLeveCompleted(Leve row) => this.unlockStateService.IsLeveCompleted(row); + public bool IsMcGuffinUnlocked(McGuffin row) => this.unlockStateService.IsMcGuffinUnlocked(row); /// <inheritdoc/> public bool IsMJILandmarkUnlocked(MJILandmark row) => this.unlockStateService.IsMJILandmarkUnlocked(row); @@ -943,9 +806,6 @@ internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockStat /// <inheritdoc/> public bool IsMKDLoreUnlocked(MKDLore row) => this.unlockStateService.IsMKDLoreUnlocked(row); - /// <inheritdoc/> - public bool IsMcGuffinUnlocked(McGuffin row) => this.unlockStateService.IsMcGuffinUnlocked(row); - /// <inheritdoc/> public bool IsMountUnlocked(Mount row) => this.unlockStateService.IsMountUnlocked(row); @@ -964,9 +824,6 @@ internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockStat /// <inheritdoc/> public bool IsPublicContentUnlocked(PublicContentSheet row) => this.unlockStateService.IsPublicContentUnlocked(row); - /// <inheritdoc/> - public bool IsQuestCompleted(Quest row) => this.unlockStateService.IsQuestCompleted(row); - /// <inheritdoc/> public bool IsRecipeUnlocked(Recipe row) => this.unlockStateService.IsRecipeUnlocked(row); @@ -979,9 +836,6 @@ internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockStat /// <inheritdoc/> public bool IsSecretRecipeBookUnlocked(SecretRecipeBook row) => this.unlockStateService.IsSecretRecipeBookUnlocked(row); - /// <inheritdoc/> - public bool IsTitleUnlocked(Title row) => this.unlockStateService.IsTitleUnlocked(row); - /// <inheritdoc/> public bool IsTraitUnlocked(Trait row) => this.unlockStateService.IsTraitUnlocked(row); diff --git a/Dalamud/GlobalSuppressions.cs b/Dalamud/GlobalSuppressions.cs index 35754eb04..8a9d31b12 100644 --- a/Dalamud/GlobalSuppressions.cs +++ b/Dalamud/GlobalSuppressions.cs @@ -21,7 +21,6 @@ using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1116:SplitParametersMustStartOnLineAfterDeclaration", Justification = "Reviewed.")] [assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleType", Justification = "This would be nice, but a big refactor")] [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:FileNameMustMatchTypeName", Justification = "I don't like this one so much")] -[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1108:BlockStatementsMustNotContainEmbeddedComments", Justification = "I like having comments in blocks")] // ImRAII stuff [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Reviewed.", Scope = "namespaceanddescendants", Target = "Dalamud.Interface.Utility.Raii")] diff --git a/Dalamud/Hooking/AsmHook.cs b/Dalamud/Hooking/AsmHook.cs index e6c9f61d8..09ae336dc 100644 --- a/Dalamud/Hooking/AsmHook.cs +++ b/Dalamud/Hooking/AsmHook.cs @@ -169,6 +169,9 @@ public sealed class AsmHook : IDisposable, IDalamudHook /// </summary> private void CheckDisposed() { - ObjectDisposedException.ThrowIf(this.IsDisposed, this); + if (this.IsDisposed) + { + throw new ObjectDisposedException(message: "Hook is already disposed", null); + } } } diff --git a/Dalamud/Hooking/Hook.cs b/Dalamud/Hooking/Hook.cs index 265b118bc..faf4658a5 100644 --- a/Dalamud/Hooking/Hook.cs +++ b/Dalamud/Hooking/Hook.cs @@ -4,9 +4,6 @@ using System.Runtime.InteropServices; using Dalamud.Configuration.Internal; using Dalamud.Hooking.Internal; -using Dalamud.Hooking.Internal.Verification; - -using TerraFX.Interop.Windows; namespace Dalamud.Hooking; @@ -22,8 +19,6 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate private const ulong IMAGE_ORDINAL_FLAG64 = 0x8000000000000000; // ReSharper disable once InconsistentNaming private const uint IMAGE_ORDINAL_FLAG32 = 0x80000000; - // ReSharper disable once InconsistentNaming - private const int IMAGE_DIRECTORY_ENTRY_IMPORT = 1; #pragma warning restore SA1310 private readonly IntPtr address; @@ -128,25 +123,25 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate module ??= Process.GetCurrentProcess().MainModule; if (module == null) throw new InvalidOperationException("Current module is null?"); - var pDos = (IMAGE_DOS_HEADER*)module.BaseAddress; - var pNt = (IMAGE_FILE_HEADER*)(module.BaseAddress + pDos->e_lfanew + 4); - var isPe64 = pNt->SizeOfOptionalHeader == Marshal.SizeOf<IMAGE_OPTIONAL_HEADER64>(); - IMAGE_DATA_DIRECTORY* pDataDirectory; + var pDos = (PeHeader.IMAGE_DOS_HEADER*)module.BaseAddress; + var pNt = (PeHeader.IMAGE_FILE_HEADER*)(module.BaseAddress + (int)pDos->e_lfanew + 4); + var isPe64 = pNt->SizeOfOptionalHeader == Marshal.SizeOf<PeHeader.IMAGE_OPTIONAL_HEADER64>(); + PeHeader.IMAGE_DATA_DIRECTORY* pDataDirectory; if (isPe64) { - var pOpt = (IMAGE_OPTIONAL_HEADER64*)(module.BaseAddress + pDos->e_lfanew + 4 + Marshal.SizeOf<IMAGE_FILE_HEADER>()); - pDataDirectory = &pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; + var pOpt = (PeHeader.IMAGE_OPTIONAL_HEADER64*)(module.BaseAddress + (int)pDos->e_lfanew + 4 + Marshal.SizeOf<PeHeader.IMAGE_FILE_HEADER>()); + pDataDirectory = &pOpt->ImportTable; } else { - var pOpt = (IMAGE_OPTIONAL_HEADER32*)(module.BaseAddress + pDos->e_lfanew + 4 + Marshal.SizeOf<IMAGE_FILE_HEADER>()); - pDataDirectory = &pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; + var pOpt = (PeHeader.IMAGE_OPTIONAL_HEADER32*)(module.BaseAddress + (int)pDos->e_lfanew + 4 + Marshal.SizeOf<PeHeader.IMAGE_FILE_HEADER>()); + pDataDirectory = &pOpt->ImportTable; } var moduleNameLowerWithNullTerminator = (moduleName + "\0").ToLowerInvariant(); - foreach (ref var importDescriptor in new Span<IMAGE_IMPORT_DESCRIPTOR>( - (IMAGE_IMPORT_DESCRIPTOR*)(module.BaseAddress + (int)pDataDirectory->VirtualAddress), - (int)(pDataDirectory->Size / Marshal.SizeOf<IMAGE_IMPORT_DESCRIPTOR>()))) + foreach (ref var importDescriptor in new Span<PeHeader.IMAGE_IMPORT_DESCRIPTOR>( + (PeHeader.IMAGE_IMPORT_DESCRIPTOR*)(module.BaseAddress + (int)pDataDirectory->VirtualAddress), + (int)(pDataDirectory->Size / Marshal.SizeOf<PeHeader.IMAGE_IMPORT_DESCRIPTOR>()))) { // Having all zero values signals the end of the table. We didn't find anything. if (importDescriptor.Characteristics == 0) @@ -164,7 +159,7 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate (int)Math.Min(pDataDirectory->Size + pDataDirectory->VirtualAddress - importDescriptor.Name, moduleNameLowerWithNullTerminator.Length)); // Is this entry about the DLL that we're looking for? (Case insensitive) - if (!currentDllNameWithNullTerminator.Equals(moduleNameLowerWithNullTerminator, StringComparison.InvariantCultureIgnoreCase)) + if (currentDllNameWithNullTerminator.ToLowerInvariant() != moduleNameLowerWithNullTerminator) continue; if (isPe64) @@ -206,19 +201,19 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate if (EnvironmentConfiguration.DalamudForceMinHook) useMinHook = true; - var moduleHandle = Windows.Win32.PInvoke.GetModuleHandle(moduleName); - if (moduleHandle.IsNull) + using var moduleHandle = Windows.Win32.PInvoke.GetModuleHandle(moduleName); + if (moduleHandle.IsInvalid) throw new Exception($"Could not get a handle to module {moduleName}"); - var procAddress = Windows.Win32.PInvoke.GetProcAddress(moduleHandle, exportName); - if (procAddress.IsNull) + var procAddress = (nint)Windows.Win32.PInvoke.GetProcAddress(moduleHandle, exportName); + if (procAddress == IntPtr.Zero) throw new Exception($"Could not get the address of {moduleName}::{exportName}"); - var address = HookManager.FollowJmp(procAddress.Value); + procAddress = HookManager.FollowJmp(procAddress); if (useMinHook) - return new MinHookHook<T>(address, detour, Assembly.GetCallingAssembly()); + return new MinHookHook<T>(procAddress, detour, Assembly.GetCallingAssembly()); else - return new ReloadedHook<T>(address, detour, Assembly.GetCallingAssembly()); + return new ReloadedHook<T>(procAddress, detour, Assembly.GetCallingAssembly()); } /// <summary> @@ -235,8 +230,6 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate if (EnvironmentConfiguration.DalamudForceMinHook) useMinHook = true; - HookVerifier.Verify<T>(procAddress); - procAddress = HookManager.FollowJmp(procAddress); if (useMinHook) return new MinHookHook<T>(procAddress, detour, Assembly.GetCallingAssembly()); @@ -249,10 +242,13 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate /// </summary> protected void CheckDisposed() { - ObjectDisposedException.ThrowIf(this.IsDisposed, this); + if (this.IsDisposed) + { + throw new ObjectDisposedException(message: "Hook is already disposed", null); + } } - private static unsafe IntPtr FromImportHelper32(IntPtr baseAddress, ref IMAGE_IMPORT_DESCRIPTOR desc, ref IMAGE_DATA_DIRECTORY dir, string functionName, uint hintOrOrdinal) + private static unsafe IntPtr FromImportHelper32(IntPtr baseAddress, ref PeHeader.IMAGE_IMPORT_DESCRIPTOR desc, ref PeHeader.IMAGE_DATA_DIRECTORY dir, string functionName, uint hintOrOrdinal) { var importLookupsOversizedSpan = new Span<uint>((uint*)(baseAddress + (int)desc.OriginalFirstThunk), (int)((dir.Size - desc.OriginalFirstThunk) / Marshal.SizeOf<int>())); var importAddressesOversizedSpan = new Span<uint>((uint*)(baseAddress + (int)desc.FirstThunk), (int)((dir.Size - desc.FirstThunk) / Marshal.SizeOf<int>())); @@ -302,7 +298,7 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate throw new MissingMethodException("Specified method not found"); } - private static unsafe IntPtr FromImportHelper64(IntPtr baseAddress, ref IMAGE_IMPORT_DESCRIPTOR desc, ref IMAGE_DATA_DIRECTORY dir, string functionName, uint hintOrOrdinal) + private static unsafe IntPtr FromImportHelper64(IntPtr baseAddress, ref PeHeader.IMAGE_IMPORT_DESCRIPTOR desc, ref PeHeader.IMAGE_DATA_DIRECTORY dir, string functionName, uint hintOrOrdinal) { var importLookupsOversizedSpan = new Span<ulong>((ulong*)(baseAddress + (int)desc.OriginalFirstThunk), (int)((dir.Size - desc.OriginalFirstThunk) / Marshal.SizeOf<ulong>())); var importAddressesOversizedSpan = new Span<ulong>((ulong*)(baseAddress + (int)desc.FirstThunk), (int)((dir.Size - desc.FirstThunk) / Marshal.SizeOf<ulong>())); diff --git a/Dalamud/Hooking/Internal/CallHook.cs b/Dalamud/Hooking/Internal/CallHook.cs new file mode 100644 index 000000000..92bc6e31a --- /dev/null +++ b/Dalamud/Hooking/Internal/CallHook.cs @@ -0,0 +1,100 @@ +using System.Runtime.InteropServices; + +using Reloaded.Hooks.Definitions; + +namespace Dalamud.Hooking.Internal; + +/// <summary> +/// This class represents a callsite hook. Only the specific address's instructions are replaced with this hook. +/// This is a destructive operation, no other callsite hooks can coexist at the same address. +/// +/// There's no .Original for this hook type. +/// This is only intended for be for functions where the parameters provided allow you to invoke the original call. +/// +/// This class was specifically added for hooking virtual function callsites. +/// Only the specific callsite hooked is modified, if the game calls the virtual function from other locations this hook will not be triggered. +/// </summary> +/// <typeparam name="T">Delegate signature for this hook.</typeparam> +internal class CallHook<T> : IDalamudHook where T : Delegate +{ + private readonly Reloaded.Hooks.AsmHook asmHook; + + private T? detour; + private bool activated; + + /// <summary> + /// Initializes a new instance of the <see cref="CallHook{T}"/> class. + /// </summary> + /// <param name="address">Address of the instruction to replace.</param> + /// <param name="detour">Delegate to invoke.</param> + internal CallHook(nint address, T detour) + { + ArgumentNullException.ThrowIfNull(detour); + + this.detour = detour; + this.Address = address; + + var detourPtr = Marshal.GetFunctionPointerForDelegate(this.detour); + var code = new[] + { + "use64", + $"mov rax, 0x{detourPtr:X8}", + "call rax", + }; + + var opt = new AsmHookOptions + { + PreferRelativeJump = true, + Behaviour = Reloaded.Hooks.Definitions.Enums.AsmHookBehaviour.DoNotExecuteOriginal, + MaxOpcodeSize = 5, + }; + + this.asmHook = new Reloaded.Hooks.AsmHook(code, (nuint)address, opt); + } + + /// <summary> + /// Gets a value indicating whether the hook is enabled. + /// </summary> + public bool IsEnabled => this.asmHook.IsEnabled; + + /// <inheritdoc/> + public IntPtr Address { get; } + + /// <inheritdoc/> + public string BackendName => "Reloaded AsmHook"; + + /// <inheritdoc/> + public bool IsDisposed => this.detour == null; + + /// <summary> + /// Starts intercepting a call to the function. + /// </summary> + public void Enable() + { + if (!this.activated) + { + this.activated = true; + this.asmHook.Activate(); + return; + } + + this.asmHook.Enable(); + } + + /// <summary> + /// Stops intercepting a call to the function. + /// </summary> + public void Disable() + { + this.asmHook.Disable(); + } + + /// <summary> + /// Remove a hook from the current process. + /// </summary> + public void Dispose() + { + this.asmHook.Disable(); + this.detour = null; + } +} diff --git a/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs b/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs index 3d1073d2f..8a53e664a 100644 --- a/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs +++ b/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs @@ -1,8 +1,8 @@ +using System.Collections.Generic; using System.Reflection; using System.Runtime.InteropServices; using JetBrains.Annotations; - using Windows.Win32.System.Memory; using Win32Exception = System.ComponentModel.Win32Exception; @@ -45,7 +45,7 @@ internal unsafe class FunctionPointerVariableHook<T> : Hook<T> if (!HookManager.MultiHookTracker.TryGetValue(this.Address, out var indexList)) { - indexList = HookManager.MultiHookTracker[this.Address] = []; + indexList = HookManager.MultiHookTracker[this.Address] = new List<IDalamudHook>(); } this.detourDelegate = detour; diff --git a/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs b/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs index 583060d53..52d266335 100644 --- a/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs +++ b/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Diagnostics; using System.Linq; using Dalamud.Game; @@ -8,7 +8,6 @@ using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Services; using Dalamud.Utility; using Dalamud.Utility.Signatures; - using Serilog; namespace Dalamud.Hooking.Internal; @@ -26,7 +25,7 @@ internal class GameInteropProviderPluginScoped : IGameInteropProvider, IInternal private readonly LocalPlugin plugin; private readonly SigScanner scanner; - private readonly WeakConcurrentCollection<IDalamudHook> trackedHooks = []; + private readonly WeakConcurrentCollection<IDalamudHook> trackedHooks = new(); /// <summary> /// Initializes a new instance of the <see cref="GameInteropProviderPluginScoped"/> class. diff --git a/Dalamud/Hooking/Internal/HookManager.cs b/Dalamud/Hooking/Internal/HookManager.cs index 83af01bf4..8f5dba583 100644 --- a/Dalamud/Hooking/Internal/HookManager.cs +++ b/Dalamud/Hooking/Internal/HookManager.cs @@ -2,7 +2,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; -using System.Threading; using Dalamud.Logging.Internal; using Dalamud.Memory; @@ -21,7 +20,7 @@ internal class HookManager : IInternalDisposableService /// <summary> /// Logger shared with <see cref="Unhooker"/>. /// </summary> - internal static readonly ModuleLog Log = ModuleLog.Create<HookManager>(); + internal static readonly ModuleLog Log = new("HM"); [ServiceManager.ServiceConstructor] private HookManager() @@ -31,7 +30,7 @@ internal class HookManager : IInternalDisposableService /// <summary> /// Gets sync root object for hook enabling/disabling. /// </summary> - internal static Lock HookEnableSyncRoot { get; } = new(); + internal static object HookEnableSyncRoot { get; } = new(); /// <summary> /// Gets a static list of tracked and registered hooks. diff --git a/Dalamud/Hooking/Internal/MinHookHook.cs b/Dalamud/Hooking/Internal/MinHookHook.cs index 9c7ec0f88..d4889ba11 100644 --- a/Dalamud/Hooking/Internal/MinHookHook.cs +++ b/Dalamud/Hooking/Internal/MinHookHook.cs @@ -24,7 +24,7 @@ internal class MinHookHook<T> : Hook<T> where T : Delegate var unhooker = HookManager.RegisterUnhooker(this.Address); if (!HookManager.MultiHookTracker.TryGetValue(this.Address, out var indexList)) - indexList = HookManager.MultiHookTracker[this.Address] = []; + indexList = HookManager.MultiHookTracker[this.Address] = new(); var index = (ulong)indexList.Count; diff --git a/Dalamud/Hooking/Internal/PeHeader.cs b/Dalamud/Hooking/Internal/PeHeader.cs new file mode 100644 index 000000000..51df4a174 --- /dev/null +++ b/Dalamud/Hooking/Internal/PeHeader.cs @@ -0,0 +1,390 @@ +using System.Runtime.InteropServices; + +#pragma warning disable +namespace Dalamud.Hooking.Internal; + +internal class PeHeader +{ + public struct IMAGE_DOS_HEADER + { + public UInt16 e_magic; + public UInt16 e_cblp; + public UInt16 e_cp; + public UInt16 e_crlc; + public UInt16 e_cparhdr; + public UInt16 e_minalloc; + public UInt16 e_maxalloc; + public UInt16 e_ss; + public UInt16 e_sp; + public UInt16 e_csum; + public UInt16 e_ip; + public UInt16 e_cs; + public UInt16 e_lfarlc; + public UInt16 e_ovno; + public UInt16 e_res_0; + public UInt16 e_res_1; + public UInt16 e_res_2; + public UInt16 e_res_3; + public UInt16 e_oemid; + public UInt16 e_oeminfo; + public UInt16 e_res2_0; + public UInt16 e_res2_1; + public UInt16 e_res2_2; + public UInt16 e_res2_3; + public UInt16 e_res2_4; + public UInt16 e_res2_5; + public UInt16 e_res2_6; + public UInt16 e_res2_7; + public UInt16 e_res2_8; + public UInt16 e_res2_9; + public UInt32 e_lfanew; + } + + [StructLayout(LayoutKind.Sequential)] + public struct IMAGE_DATA_DIRECTORY + { + public UInt32 VirtualAddress; + public UInt32 Size; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct IMAGE_OPTIONAL_HEADER32 + { + public UInt16 Magic; + public Byte MajorLinkerVersion; + public Byte MinorLinkerVersion; + public UInt32 SizeOfCode; + public UInt32 SizeOfInitializedData; + public UInt32 SizeOfUninitializedData; + public UInt32 AddressOfEntryPoint; + public UInt32 BaseOfCode; + public UInt32 BaseOfData; + public UInt32 ImageBase; + public UInt32 SectionAlignment; + public UInt32 FileAlignment; + public UInt16 MajorOperatingSystemVersion; + public UInt16 MinorOperatingSystemVersion; + public UInt16 MajorImageVersion; + public UInt16 MinorImageVersion; + public UInt16 MajorSubsystemVersion; + public UInt16 MinorSubsystemVersion; + public UInt32 Win32VersionValue; + public UInt32 SizeOfImage; + public UInt32 SizeOfHeaders; + public UInt32 CheckSum; + public UInt16 Subsystem; + public UInt16 DllCharacteristics; + public UInt32 SizeOfStackReserve; + public UInt32 SizeOfStackCommit; + public UInt32 SizeOfHeapReserve; + public UInt32 SizeOfHeapCommit; + public UInt32 LoaderFlags; + public UInt32 NumberOfRvaAndSizes; + + public IMAGE_DATA_DIRECTORY ExportTable; + public IMAGE_DATA_DIRECTORY ImportTable; + public IMAGE_DATA_DIRECTORY ResourceTable; + public IMAGE_DATA_DIRECTORY ExceptionTable; + public IMAGE_DATA_DIRECTORY CertificateTable; + public IMAGE_DATA_DIRECTORY BaseRelocationTable; + public IMAGE_DATA_DIRECTORY Debug; + public IMAGE_DATA_DIRECTORY Architecture; + public IMAGE_DATA_DIRECTORY GlobalPtr; + public IMAGE_DATA_DIRECTORY TLSTable; + public IMAGE_DATA_DIRECTORY LoadConfigTable; + public IMAGE_DATA_DIRECTORY BoundImport; + public IMAGE_DATA_DIRECTORY IAT; + public IMAGE_DATA_DIRECTORY DelayImportDescriptor; + public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; + public IMAGE_DATA_DIRECTORY Reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct IMAGE_OPTIONAL_HEADER64 + { + public UInt16 Magic; + public Byte MajorLinkerVersion; + public Byte MinorLinkerVersion; + public UInt32 SizeOfCode; + public UInt32 SizeOfInitializedData; + public UInt32 SizeOfUninitializedData; + public UInt32 AddressOfEntryPoint; + public UInt32 BaseOfCode; + public UInt64 ImageBase; + public UInt32 SectionAlignment; + public UInt32 FileAlignment; + public UInt16 MajorOperatingSystemVersion; + public UInt16 MinorOperatingSystemVersion; + public UInt16 MajorImageVersion; + public UInt16 MinorImageVersion; + public UInt16 MajorSubsystemVersion; + public UInt16 MinorSubsystemVersion; + public UInt32 Win32VersionValue; + public UInt32 SizeOfImage; + public UInt32 SizeOfHeaders; + public UInt32 CheckSum; + public UInt16 Subsystem; + public UInt16 DllCharacteristics; + public UInt64 SizeOfStackReserve; + public UInt64 SizeOfStackCommit; + public UInt64 SizeOfHeapReserve; + public UInt64 SizeOfHeapCommit; + public UInt32 LoaderFlags; + public UInt32 NumberOfRvaAndSizes; + + public IMAGE_DATA_DIRECTORY ExportTable; + public IMAGE_DATA_DIRECTORY ImportTable; + public IMAGE_DATA_DIRECTORY ResourceTable; + public IMAGE_DATA_DIRECTORY ExceptionTable; + public IMAGE_DATA_DIRECTORY CertificateTable; + public IMAGE_DATA_DIRECTORY BaseRelocationTable; + public IMAGE_DATA_DIRECTORY Debug; + public IMAGE_DATA_DIRECTORY Architecture; + public IMAGE_DATA_DIRECTORY GlobalPtr; + public IMAGE_DATA_DIRECTORY TLSTable; + public IMAGE_DATA_DIRECTORY LoadConfigTable; + public IMAGE_DATA_DIRECTORY BoundImport; + public IMAGE_DATA_DIRECTORY IAT; + public IMAGE_DATA_DIRECTORY DelayImportDescriptor; + public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; + public IMAGE_DATA_DIRECTORY Reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct IMAGE_FILE_HEADER + { + public UInt16 Machine; + public UInt16 NumberOfSections; + public UInt32 TimeDateStamp; + public UInt32 PointerToSymbolTable; + public UInt32 NumberOfSymbols; + public UInt16 SizeOfOptionalHeader; + public UInt16 Characteristics; + } + + [StructLayout(LayoutKind.Explicit)] + public struct IMAGE_SECTION_HEADER + { + [FieldOffset(0)] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public char[] Name; + [FieldOffset(8)] + public UInt32 VirtualSize; + [FieldOffset(12)] + public UInt32 VirtualAddress; + [FieldOffset(16)] + public UInt32 SizeOfRawData; + [FieldOffset(20)] + public UInt32 PointerToRawData; + [FieldOffset(24)] + public UInt32 PointerToRelocations; + [FieldOffset(28)] + public UInt32 PointerToLinenumbers; + [FieldOffset(32)] + public UInt16 NumberOfRelocations; + [FieldOffset(34)] + public UInt16 NumberOfLinenumbers; + [FieldOffset(36)] + public DataSectionFlags Characteristics; + + public string Section + { + get { return new string(Name); } + } + } + + [Flags] + public enum DataSectionFlags : uint + { + /// <summary> + /// Reserved for future use. + /// </summary> + TypeReg = 0x00000000, + /// <summary> + /// Reserved for future use. + /// </summary> + TypeDsect = 0x00000001, + /// <summary> + /// Reserved for future use. + /// </summary> + TypeNoLoad = 0x00000002, + /// <summary> + /// Reserved for future use. + /// </summary> + TypeGroup = 0x00000004, + /// <summary> + /// The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files. + /// </summary> + TypeNoPadded = 0x00000008, + /// <summary> + /// Reserved for future use. + /// </summary> + TypeCopy = 0x00000010, + /// <summary> + /// The section contains executable code. + /// </summary> + ContentCode = 0x00000020, + /// <summary> + /// The section contains initialized data. + /// </summary> + ContentInitializedData = 0x00000040, + /// <summary> + /// The section contains uninitialized data. + /// </summary> + ContentUninitializedData = 0x00000080, + /// <summary> + /// Reserved for future use. + /// </summary> + LinkOther = 0x00000100, + /// <summary> + /// The section contains comments or other information. The .drectve section has this type. This is valid for object files only. + /// </summary> + LinkInfo = 0x00000200, + /// <summary> + /// Reserved for future use. + /// </summary> + TypeOver = 0x00000400, + /// <summary> + /// The section will not become part of the image. This is valid only for object files. + /// </summary> + LinkRemove = 0x00000800, + /// <summary> + /// The section contains COMDAT data. For more information, see section 5.5.6, COMDAT Sections (Object Only). This is valid only for object files. + /// </summary> + LinkComDat = 0x00001000, + /// <summary> + /// Reset speculative exceptions handling bits in the TLB entries for this section. + /// </summary> + NoDeferSpecExceptions = 0x00004000, + /// <summary> + /// The section contains data referenced through the global pointer (GP). + /// </summary> + RelativeGP = 0x00008000, + /// <summary> + /// Reserved for future use. + /// </summary> + MemPurgeable = 0x00020000, + /// <summary> + /// Reserved for future use. + /// </summary> + Memory16Bit = 0x00020000, + /// <summary> + /// Reserved for future use. + /// </summary> + MemoryLocked = 0x00040000, + /// <summary> + /// Reserved for future use. + /// </summary> + MemoryPreload = 0x00080000, + /// <summary> + /// Align data on a 1-byte boundary. Valid only for object files. + /// </summary> + Align1Bytes = 0x00100000, + /// <summary> + /// Align data on a 2-byte boundary. Valid only for object files. + /// </summary> + Align2Bytes = 0x00200000, + /// <summary> + /// Align data on a 4-byte boundary. Valid only for object files. + /// </summary> + Align4Bytes = 0x00300000, + /// <summary> + /// Align data on an 8-byte boundary. Valid only for object files. + /// </summary> + Align8Bytes = 0x00400000, + /// <summary> + /// Align data on a 16-byte boundary. Valid only for object files. + /// </summary> + Align16Bytes = 0x00500000, + /// <summary> + /// Align data on a 32-byte boundary. Valid only for object files. + /// </summary> + Align32Bytes = 0x00600000, + /// <summary> + /// Align data on a 64-byte boundary. Valid only for object files. + /// </summary> + Align64Bytes = 0x00700000, + /// <summary> + /// Align data on a 128-byte boundary. Valid only for object files. + /// </summary> + Align128Bytes = 0x00800000, + /// <summary> + /// Align data on a 256-byte boundary. Valid only for object files. + /// </summary> + Align256Bytes = 0x00900000, + /// <summary> + /// Align data on a 512-byte boundary. Valid only for object files. + /// </summary> + Align512Bytes = 0x00A00000, + /// <summary> + /// Align data on a 1024-byte boundary. Valid only for object files. + /// </summary> + Align1024Bytes = 0x00B00000, + /// <summary> + /// Align data on a 2048-byte boundary. Valid only for object files. + /// </summary> + Align2048Bytes = 0x00C00000, + /// <summary> + /// Align data on a 4096-byte boundary. Valid only for object files. + /// </summary> + Align4096Bytes = 0x00D00000, + /// <summary> + /// Align data on an 8192-byte boundary. Valid only for object files. + /// </summary> + Align8192Bytes = 0x00E00000, + /// <summary> + /// The section contains extended relocations. + /// </summary> + LinkExtendedRelocationOverflow = 0x01000000, + /// <summary> + /// The section can be discarded as needed. + /// </summary> + MemoryDiscardable = 0x02000000, + /// <summary> + /// The section cannot be cached. + /// </summary> + MemoryNotCached = 0x04000000, + /// <summary> + /// The section is not pageable. + /// </summary> + MemoryNotPaged = 0x08000000, + /// <summary> + /// The section can be shared in memory. + /// </summary> + MemoryShared = 0x10000000, + /// <summary> + /// The section can be executed as code. + /// </summary> + MemoryExecute = 0x20000000, + /// <summary> + /// The section can be read. + /// </summary> + MemoryRead = 0x40000000, + /// <summary> + /// The section can be written to. + /// </summary> + MemoryWrite = 0x80000000 + } + + [StructLayout(LayoutKind.Explicit)] + public struct IMAGE_IMPORT_DESCRIPTOR + { + [FieldOffset(0)] + public uint Characteristics; + + [FieldOffset(0)] + public uint OriginalFirstThunk; + + [FieldOffset(4)] + public uint TimeDateStamp; + + [FieldOffset(8)] + public uint ForwarderChain; + + [FieldOffset(12)] + public uint Name; + + [FieldOffset(16)] + public uint FirstThunk; + } +} diff --git a/Dalamud/Hooking/Internal/Verification/HookVerificationException.cs b/Dalamud/Hooking/Internal/Verification/HookVerificationException.cs deleted file mode 100644 index c43b5d540..000000000 --- a/Dalamud/Hooking/Internal/Verification/HookVerificationException.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Linq; - -namespace Dalamud.Hooking.Internal.Verification; - -/// <summary> -/// Exception thrown when a provided delegate for a hook does not match a known delegate. -/// </summary> -public class HookVerificationException : Exception -{ - private HookVerificationException(string message) - : base(message) - { - } - - /// <summary> - /// Create a new <see cref="HookVerificationException"/> exception. - /// </summary> - /// <param name="address">The address of the function that is being hooked.</param> - /// <param name="passed">The delegate passed by the user.</param> - /// <param name="enforced">The delegate we think is correct.</param> - /// <param name="message">Additional context to show to the user.</param> - /// <returns>The created exception.</returns> - internal static HookVerificationException Create(IntPtr address, Type passed, Type enforced, string message) - { - return new HookVerificationException( - $"Hook verification failed for address 0x{address.ToInt64():X}\n\n" + - $"Why: {message}\n" + - $"Passed Delegate: {GetSignature(passed)}\n" + - $"Correct Delegate: {GetSignature(enforced)}\n\n" + - "The hook delegate must exactly match the provided signature to prevent memory corruption and wrong data passed to originals."); - } - - private static string GetSignature(Type delegateType) - { - var method = delegateType.GetMethod("Invoke"); - if (method == null) return delegateType.Name; - - var parameters = string.Join(", ", method.GetParameters().Select(p => p.ParameterType.Name)); - return $"{method.ReturnType.Name} ({parameters})"; - } -} diff --git a/Dalamud/Hooking/Internal/Verification/HookVerifier.cs b/Dalamud/Hooking/Internal/Verification/HookVerifier.cs deleted file mode 100644 index 721798a40..000000000 --- a/Dalamud/Hooking/Internal/Verification/HookVerifier.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -using Dalamud.Configuration.Internal; -using Dalamud.Game; -using Dalamud.Logging.Internal; - -using FFXIVClientStructs.FFXIV.Application.Network; - -using InteropGenerator.Runtime; - -namespace Dalamud.Hooking.Internal.Verification; - -/// <summary> -/// Global utility that can verify whether hook delegates are correctly declared. -/// Initialized out-of-band, since Hook is instantiated all over the place without a service, so this cannot be -/// a service either. -/// </summary> -internal static class HookVerifier -{ - private static readonly ModuleLog Log = new("HookVerifier"); - - private static readonly VerificationEntry[] ToVerify = - [ - new( - "ActorControlSelf", - "E8 ?? ?? ?? ?? 0F B7 0B 83 E9 64", - typeof(ActorControlSelfDelegate), // TODO: change this to CS delegate - "Signature changed in Patch 7.4"), // 7.4 (new parameters) - new( - "SendPacket", - ZoneClient.Addresses.SendPacket.String, - typeof(ZoneClient.Delegates.SendPacket), - "Force marshaling context") // If people hook with 4 byte return this locks people out from logging in - ]; - - private static readonly string ClientStructsInteropNamespacePrefix = string.Join(".", nameof(FFXIVClientStructs), nameof(FFXIVClientStructs.Interop)); - - private delegate void ActorControlSelfDelegate(uint category, uint eventId, uint param1, uint param2, uint param3, uint param4, uint param5, uint param6, uint param7, uint param8, ulong targetId, byte param9); // TODO: change this to CS delegate - - /// <summary> - /// Initializes a new instance of the <see cref="HookVerifier"/> class. - /// </summary> - /// <param name="scanner">Process to scan in.</param> - public static void Initialize(TargetSigScanner scanner) - { - foreach (var entry in ToVerify) - { - if (!scanner.TryScanText(entry.Signature, out var address)) - { - Log.Error("Could not resolve signature for hook {Name} ({Sig})", entry.Name, entry.Signature); - continue; - } - - entry.Address = address; - } - } - - /// <summary> - /// Verify the hook with the provided address and exception. - /// </summary> - /// <param name="address">The address of the function we are hooking.</param> - /// <typeparam name="T">The delegate type passed by the creator of the hook.</typeparam> - /// <exception cref="HookVerificationException">Exception thrown when we think the hook is not correctly declared.</exception> - public static void Verify<T>(IntPtr address) where T : Delegate - { - // API15 TODO: Always throw - var config = Service<DalamudConfiguration>.GetNullable(); - if (config != null && config.DevPluginLoadLocations.Count == 0) - { - return; - } - - var entry = ToVerify.FirstOrDefault(x => x.Address == address); - - // Nothing to verify for this hook? - if (entry == null) - { - return; - } - - var passedType = typeof(T); - var isAssemblyMarshaled = passedType.Assembly.GetCustomAttribute<DisableRuntimeMarshallingAttribute>() is null; - - // Directly compare delegates - if (passedType == entry.TargetDelegateType) - { - return; - } - - var passedInvoke = passedType.GetMethod("Invoke")!; - var enforcedInvoke = entry.TargetDelegateType.GetMethod("Invoke")!; - - // Compare Return Type - var mismatch = !CheckParam(passedInvoke.ReturnType, enforcedInvoke.ReturnType, isAssemblyMarshaled); - - // Compare Parameter Count - var passedParams = passedInvoke.GetParameters(); - var enforcedParams = enforcedInvoke.GetParameters(); - - if (passedParams.Length != enforcedParams.Length) - { - mismatch = true; - } - else - { - // Compare Parameter Types - for (var i = 0; i < passedParams.Length; i++) - { - if (!CheckParam(passedParams[i].ParameterType, enforcedParams[i].ParameterType, isAssemblyMarshaled)) - { - mismatch = true; - break; - } - } - } - - if (mismatch) - { - throw HookVerificationException.Create(address, passedType, entry.TargetDelegateType, entry.Message); - } - } - - private static bool CheckParam(Type paramLeft, Type paramRight, bool isMarshaled) - { - var sameType = paramLeft == paramRight; - return sameType || SizeOf(paramLeft, isMarshaled) == SizeOf(paramRight, false); - } - - private static int SizeOf(Type type, bool isMarshaled) - { - return type switch { - _ when type == typeof(sbyte) || type == typeof(byte) || (type == typeof(bool) && !isMarshaled) => 1, - _ when type == typeof(char) || type == typeof(short) || type == typeof(ushort) || type == typeof(Half) => 2, - _ when type == typeof(int) || type == typeof(uint) || type == typeof(float) || (type == typeof(bool) && isMarshaled) => 4, - _ when type == typeof(long) || type == typeof(ulong) || type == typeof(double) || type.IsPointer || type.IsFunctionPointer || type.IsUnmanagedFunctionPointer || (type.Name == "Pointer`1" && type.Namespace.AsSpan().SequenceEqual(ClientStructsInteropNamespacePrefix)) || type == typeof(CStringPointer) => 8, - _ when type.Name.StartsWith("FixedSizeArray") => SizeOf(type.GetGenericArguments()[0], isMarshaled) * int.Parse(type.Name[14..type.Name.IndexOf('`')]), - _ when type.GetCustomAttribute<InlineArrayAttribute>() is { Length: var length } => SizeOf(type.GetGenericArguments()[0], isMarshaled) * length, - _ when IsStruct(type) && !type.IsGenericType && (type.StructLayoutAttribute?.Value ?? LayoutKind.Sequential) != LayoutKind.Sequential => type.StructLayoutAttribute?.Size ?? (int?)typeof(Unsafe).GetMethod("SizeOf")?.MakeGenericMethod(type).Invoke(null, null) ?? 0, - _ when type.IsEnum => SizeOf(Enum.GetUnderlyingType(type), isMarshaled), - _ when type.IsGenericType => Marshal.SizeOf(Activator.CreateInstance(type)!), - _ => GetSizeOf(type), - }; - } - - private static int GetSizeOf(Type type) - { - try - { - return Marshal.SizeOf(Activator.CreateInstance(type)!); - } - catch - { - return 0; - } - } - - private static bool IsStruct(Type type) - { - return type != typeof(decimal) && type is { IsValueType: true, IsPrimitive: false, IsEnum: false }; - } - - private record VerificationEntry(string Name, string Signature, Type TargetDelegateType, string Message) - { - public nint Address { get; set; } - } -} diff --git a/Dalamud/Hooking/WndProcHook/WndProcHookManager.cs b/Dalamud/Hooking/WndProcHook/WndProcHookManager.cs index 82620eed8..7c70a9f0b 100644 --- a/Dalamud/Hooking/WndProcHook/WndProcHookManager.cs +++ b/Dalamud/Hooking/WndProcHook/WndProcHookManager.cs @@ -17,10 +17,10 @@ namespace Dalamud.Hooking.WndProcHook; [ServiceManager.EarlyLoadedService] internal sealed class WndProcHookManager : IInternalDisposableService { - private static readonly ModuleLog Log = ModuleLog.Create<WndProcHookManager>(); + private static readonly ModuleLog Log = new(nameof(WndProcHookManager)); private readonly Hook<DispatchMessageWDelegate> dispatchMessageWHook; - private readonly Dictionary<HWND, WndProcEventArgs> wndProcOverrides = []; + private readonly Dictionary<HWND, WndProcEventArgs> wndProcOverrides = new(); private HWND mainWindowHwnd; diff --git a/Dalamud/Interface/Animation/Easing.cs b/Dalamud/Interface/Animation/Easing.cs index a9dfad1f0..0d2057b3b 100644 --- a/Dalamud/Interface/Animation/Easing.cs +++ b/Dalamud/Interface/Animation/Easing.cs @@ -48,7 +48,7 @@ public abstract class Easing /// Gets the current value of the animation, following unclamped logic. /// </summary> [Obsolete($"This field has been deprecated. Use either {nameof(ValueClamped)} or {nameof(ValueUnclamped)} instead.", true)] - [Api15ToDo("Map this field to ValueClamped, probably.")] + [Api13ToDo("Map this field to ValueClamped, probably.")] public double Value => this.ValueUnclamped; /// <summary> diff --git a/Dalamud/Interface/ColorHelpers.cs b/Dalamud/Interface/ColorHelpers.cs index dd56f3d14..a3ae6799e 100644 --- a/Dalamud/Interface/ColorHelpers.cs +++ b/Dalamud/Interface/ColorHelpers.cs @@ -56,10 +56,11 @@ public static class ColorHelpers var min = Math.Min(r, Math.Min(g, b)); var h = max; + var s = max; var v = max; var d = max - min; - var s = max == 0 ? 0 : d / max; + s = max == 0 ? 0 : d / max; if (max == min) { diff --git a/Dalamud/Interface/Components/ImGuiComponents.HelpMarker.cs b/Dalamud/Interface/Components/ImGuiComponents.HelpMarker.cs index 0d919cf6e..57a4bd150 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.HelpMarker.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.HelpMarker.cs @@ -1,6 +1,5 @@ using Dalamud.Bindings.ImGui; using Dalamud.Interface.Utility.Raii; - using FFXIVClientStructs.FFXIV.Common.Math; namespace Dalamud.Interface.Components; diff --git a/Dalamud/Interface/DalamudWindowOpenKinds.cs b/Dalamud/Interface/DalamudWindowOpenKinds.cs index 891f9281a..35d2825f7 100644 --- a/Dalamud/Interface/DalamudWindowOpenKinds.cs +++ b/Dalamud/Interface/DalamudWindowOpenKinds.cs @@ -56,11 +56,6 @@ public enum SettingsOpenKind /// </summary> ServerInfoBar, - /// <summary> - /// Open to the "Badges" page. - /// </summary> - Badge, - /// <summary> /// Open to the "Experimental" page. /// </summary> diff --git a/Dalamud/Interface/DragDrop/DragDropManager.cs b/Dalamud/Interface/DragDrop/DragDropManager.cs index 504bdc716..4375ddea9 100644 --- a/Dalamud/Interface/DragDrop/DragDropManager.cs +++ b/Dalamud/Interface/DragDrop/DragDropManager.cs @@ -5,7 +5,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Interface.Internal; using Dalamud.IoC; using Dalamud.IoC.Internal; - using Serilog; namespace Dalamud.Interface.DragDrop; diff --git a/Dalamud/Interface/DragDrop/DragDropTarget.cs b/Dalamud/Interface/DragDrop/DragDropTarget.cs index 8ac347c72..c6b66e7e8 100644 --- a/Dalamud/Interface/DragDrop/DragDropTarget.cs +++ b/Dalamud/Interface/DragDrop/DragDropTarget.cs @@ -6,7 +6,6 @@ using System.Text; using Dalamud.Bindings.ImGui; using Dalamud.Utility; - using Serilog; namespace Dalamud.Interface.DragDrop; diff --git a/Dalamud/Interface/FontAwesome/FontAwesomeExtensions.cs b/Dalamud/Interface/FontAwesome/FontAwesomeExtensions.cs index 0bab4538a..2b8cb8c52 100644 --- a/Dalamud/Interface/FontAwesome/FontAwesomeExtensions.cs +++ b/Dalamud/Interface/FontAwesome/FontAwesomeExtensions.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Utility; @@ -37,7 +37,7 @@ public static class FontAwesomeExtensions public static IEnumerable<string> GetSearchTerms(this FontAwesomeIcon icon) { var searchTermsAttribute = icon.GetAttribute<FontAwesomeSearchTermsAttribute>(); - return searchTermsAttribute == null ? [] : searchTermsAttribute.SearchTerms; + return searchTermsAttribute == null ? new string[] { } : searchTermsAttribute.SearchTerms; } /// <summary> @@ -48,6 +48,6 @@ public static class FontAwesomeExtensions public static IEnumerable<string> GetCategories(this FontAwesomeIcon icon) { var categoriesAttribute = icon.GetAttribute<FontAwesomeCategoriesAttribute>(); - return categoriesAttribute == null ? [] : categoriesAttribute.Categories; + return categoriesAttribute == null ? new string[] { } : categoriesAttribute.Categories; } } diff --git a/Dalamud/Interface/FontAwesome/FontAwesomeHelpers.cs b/Dalamud/Interface/FontAwesome/FontAwesomeHelpers.cs index 51c896ad2..cf34d736e 100644 --- a/Dalamud/Interface/FontAwesome/FontAwesomeHelpers.cs +++ b/Dalamud/Interface/FontAwesome/FontAwesomeHelpers.cs @@ -16,7 +16,14 @@ public static class FontAwesomeHelpers /// <returns>list of font awesome icons.</returns> public static List<FontAwesomeIcon> GetIcons() { - return [.. Enum.GetValues<FontAwesomeIcon>().Where(icon => !icon.IsObsolete())]; + var icons = new List<FontAwesomeIcon>(); + foreach (var icon in Enum.GetValues(typeof(FontAwesomeIcon)).Cast<FontAwesomeIcon>().ToList()) + { + if (icon.IsObsolete()) continue; + icons.Add(icon); + } + + return icons; } /// <summary> @@ -68,7 +75,7 @@ public static class FontAwesomeHelpers { var name = Enum.GetName(icon)?.ToLowerInvariant(); var searchTerms = icon.GetSearchTerms(); - if (name!.Contains(search, StringComparison.InvariantCultureIgnoreCase) || searchTerms.Contains(search.ToLowerInvariant())) + if (name!.Contains(search.ToLowerInvariant()) || searchTerms.Contains(search.ToLowerInvariant())) { result.Add(icon); } @@ -98,7 +105,7 @@ public static class FontAwesomeHelpers var name = Enum.GetName(icon)?.ToLowerInvariant(); var searchTerms = icon.GetSearchTerms(); var categories = icon.GetCategories(); - if ((name!.Contains(search, StringComparison.InvariantCultureIgnoreCase) || searchTerms.Contains(search.ToLowerInvariant())) && categories.Contains(category)) + if ((name!.Contains(search.ToLowerInvariant()) || searchTerms.Contains(search.ToLowerInvariant())) && categories.Contains(category)) { result.Add(icon); } diff --git a/Dalamud/Interface/FontAwesome/FontAwesomeIcon.cs b/Dalamud/Interface/FontAwesome/FontAwesomeIcon.cs index 35df9cfbc..f88d7f8f0 100644 --- a/Dalamud/Interface/FontAwesome/FontAwesomeIcon.cs +++ b/Dalamud/Interface/FontAwesome/FontAwesomeIcon.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // Generated by Dalamud.FASharpGen - don't modify this file directly. -// Font-Awesome Version: 7.1.0 +// Font-Awesome Version: 6.4.2 // </auto-generated> //------------------------------------------------------------------------------ @@ -29,14 +29,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "address-book" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "address book", "contact", "directory", "employee", "index", "little black book", "portfolio", "rolodex", "uer", "username" })] + [FontAwesomeSearchTerms(new[] { "address book", "contact", "directory", "index", "little black book", "rolodex" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Communication", "Users + People" })] AddressBook = 0xF2B9, /// <summary> /// The Font Awesome "address-card" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "address card", "about", "contact", "employee", "id", "identification", "portfolio", "postcard", "profile", "registration", "uer", "username" })] + [FontAwesomeSearchTerms(new[] { "address card", "about", "contact", "id", "identification", "postcard", "profile", "registration" })] [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Alphabet", "Business", "Communication", "Users + People" })] AddressCard = 0xF2BB, @@ -54,13 +54,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Automotive" })] AirFreshener = 0xF5D0, - /// <summary> - /// The Font Awesome "alarm-clock" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "alarm clock", "alarm", "alarm clock", "clock", "date", "late", "pending", "reminder", "sleep", "snooze", "timer", "timestamp", "watch" })] - [FontAwesomeCategoriesAttribute(new[] { "Alert", "Time", "Travel + Hotel" })] - AlarmClock = 0xF34E, - /// <summary> /// The Font Awesome "align-center" icon unicode character. /// </summary> @@ -120,28 +113,28 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "anchor-circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "anchor circle check", "enable", "marina", "not affected", "ok", "okay", "port", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "anchor circle check", "marina", "not affected", "ok", "okay", "port" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics", "Maritime" })] AnchorCircleCheck = 0xE4AA, /// <summary> /// The Font Awesome "anchor-circle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "anchor circle exclamation", "affected", "failed", "marina", "port" })] + [FontAwesomeSearchTerms(new[] { "anchor circle exclamation", "affected", "marina", "port" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics", "Maritime" })] AnchorCircleExclamation = 0xE4AB, /// <summary> /// The Font Awesome "anchor-circle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "anchor circle xmark", "destroy", "marina", "port", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "anchor circle xmark", "destroy", "marina", "port" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics", "Maritime" })] AnchorCircleXmark = 0xE4AC, /// <summary> /// The Font Awesome "anchor-lock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "anchor lock", "closed", "lockdown", "marina", "padlock", "port", "privacy", "quarantine" })] + [FontAwesomeSearchTerms(new[] { "anchor lock", "closed", "lockdown", "marina", "port", "quarantine" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics", "Maritime" })] AnchorLock = 0xE4AD, @@ -176,7 +169,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "angle-down" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "angle down", "down arrowhead", "arrow", "caret", "download", "expand", "insert" })] + [FontAwesomeSearchTerms(new[] { "angle down", "down arrowhead", "arrow", "caret", "download", "expand" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] AngleDown = 0xF107, @@ -197,7 +190,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "angle-up" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "angle up", "up arrowhead", "arrow", "caret", "collapse", "upgrade", "upload" })] + [FontAwesomeSearchTerms(new[] { "angle up", "up arrowhead", "arrow", "caret", "collapse", "upload" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] AngleUp = 0xF106, @@ -260,7 +253,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "circle-up" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "circle up", "arrow-circle-o-up", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "circle up", "arrow-circle-o-up" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] ArrowAltCircleUp = 0xF35B, @@ -288,7 +281,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "circle-arrow-up" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "circle arrow up", "upgrade", "upload" })] + [FontAwesomeSearchTerms(new[] { "circle arrow up", "upload" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] ArrowCircleUp = 0xF0AA, @@ -316,7 +309,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "arrow-down-up-lock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrow down up lock", "border", "closed", "crossing", "lockdown", "padlock", "privacy", "quarantine", "transfer" })] + [FontAwesomeSearchTerms(new[] { "arrow down up lock", "border", "closed", "crossing", "lockdown", "quarantine", "transfer" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Humanitarian" })] ArrowDownUpLock = 0xE4B0, @@ -344,7 +337,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "arrow-right-arrow-left" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrow right arrow left", "arrow", "arrows", "reciprocate", "return", "swap", "transfer" })] + [FontAwesomeSearchTerms(new[] { "arrow right arrow left", "rightwards arrow over leftwards arrow", "arrow", "arrows", "reciprocate", "return", "swap", "transfer" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] ArrowRightArrowLeft = 0xF0EC, @@ -365,14 +358,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "arrow-right-to-bracket" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrow right to bracket", "arrow", "enter", "insert", "join", "log in", "login", "sign in", "sign up", "sign-in", "signin", "signup" })] + [FontAwesomeSearchTerms(new[] { "arrow right to bracket", "arrow", "enter", "join", "log in", "login", "sign in", "sign up", "sign-in", "signin", "signup" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] ArrowRightToBracket = 0xF090, /// <summary> /// The Font Awesome "arrow-right-to-city" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrow right to city", "building", "city", "exodus", "insert", "rural", "urban" })] + [FontAwesomeSearchTerms(new[] { "arrow right to city", "building", "city", "exodus", "rural", "urban" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian" })] ArrowRightToCity = 0xE4B3, @@ -400,14 +393,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "arrows-down-to-line" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrows down to line", "insert", "scale down", "sink" })] + [FontAwesomeSearchTerms(new[] { "arrows down to line", "scale down", "sink" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Humanitarian" })] ArrowsDownToLine = 0xE4B8, /// <summary> /// The Font Awesome "arrows-down-to-people" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrows down to people", "affected", "focus", "insert", "targeted", "together", "uer" })] + [FontAwesomeSearchTerms(new[] { "arrows down to people", "affected", "focus", "targeted" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] ArrowsDownToPeople = 0xE4B9, @@ -442,14 +435,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "arrows-to-circle" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrows to circle", "center", "concentrate", "coordinate", "coordination", "focal point", "focus", "insert" })] + [FontAwesomeSearchTerms(new[] { "arrows to circle", "center", "concentrate", "coordinate", "coordination", "focal point", "focus" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Humanitarian" })] ArrowsToCircle = 0xE4BD, /// <summary> /// The Font Awesome "arrows-to-dot" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrows to dot", "assembly point", "center", "condense", "focus", "insert", "minimize" })] + [FontAwesomeSearchTerms(new[] { "arrows to dot", "assembly point", "center", "condense", "focus", "minimize" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Business", "Humanitarian", "Marketing" })] ArrowsToDot = 0xE4BE, @@ -470,7 +463,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "arrows-turn-to-dots" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrows turn to dots", "destination", "insert", "nexus" })] + [FontAwesomeSearchTerms(new[] { "arrows turn to dots", "destination", "nexus" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Humanitarian" })] ArrowsTurnToDots = 0xE4C1, @@ -491,7 +484,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "arrows-up-to-line" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrows up to line", "rise", "scale up", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "arrows up to line", "rise", "scale up" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Humanitarian" })] ArrowsUpToLine = 0xE4C2, @@ -526,28 +519,28 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "arrow-up" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrow up", "upwards arrow", "forward", "upgrade", "upload" })] + [FontAwesomeSearchTerms(new[] { "arrow up", "upwards arrow", "forward", "upload" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] ArrowUp = 0xF062, /// <summary> /// The Font Awesome "arrow-up-from-bracket" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrow up from bracket", "share", "transfer", "upgrade", "upload" })] + [FontAwesomeSearchTerms(new[] { "arrow up from bracket", "share", "transfer", "upload" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] ArrowUpFromBracket = 0xE09A, /// <summary> /// The Font Awesome "arrow-up-from-ground-water" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrow up from ground water", "groundwater", "spring", "upgrade", "water supply", "water table" })] + [FontAwesomeSearchTerms(new[] { "arrow up from ground water", "groundwater", "spring", "water supply", "water table" })] [FontAwesomeCategoriesAttribute(new[] { "Construction", "Energy", "Humanitarian" })] ArrowUpFromGroundWater = 0xE4B5, /// <summary> /// The Font Awesome "arrow-up-from-water-pump" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrow up from water pump", "flood", "groundwater", "pump", "submersible", "sump pump", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "arrow up from water pump", "flood", "groundwater", "pump", "submersible", "sump pump" })] [FontAwesomeCategoriesAttribute(new[] { "Household", "Humanitarian" })] ArrowUpFromWaterPump = 0xE4B6, @@ -561,14 +554,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "arrow-up-right-dots" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrow up right dots", "growth", "increase", "population", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "arrow up right dots", "growth", "increase", "population" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Humanitarian" })] ArrowUpRightDots = 0xE4B7, /// <summary> /// The Font Awesome "arrow-up-right-from-square" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrow up right from square", "new", "open", "send", "share", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "arrow up right from square", "new", "open", "send", "share" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Humanitarian" })] ArrowUpRightFromSquare = 0xF08E, @@ -583,7 +576,7 @@ public enum FontAwesomeIcon /// The Font Awesome "asterisk" icon unicode character. /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0x2A. /// </summary> - [FontAwesomeSearchTerms(new[] { "asterisk", "asterisk", "heavy asterisk", "annotation", "details", "reference", "required", "star" })] + [FontAwesomeSearchTerms(new[] { "asterisk", "asterisk", "heavy asterisk", "annotation", "details", "reference", "star" })] [FontAwesomeCategoriesAttribute(new[] { "Punctuation + Symbols", "Spinners" })] Asterisk = 0xF069, @@ -598,14 +591,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "book-atlas" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "book atlas", "book", "directions", "geography", "globe", "knowledge", "library", "map", "research", "travel", "wayfinding" })] + [FontAwesomeSearchTerms(new[] { "book atlas", "book", "directions", "geography", "globe", "library", "map", "research", "travel", "wayfinding" })] [FontAwesomeCategoriesAttribute(new[] { "Maps", "Travel + Hotel" })] Atlas = 0xF558, /// <summary> /// The Font Awesome "atom" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "atheism", "atheist", "atom", "atom symbol", "chemistry", "electron", "ion", "isotope", "knowledge", "neutron", "nuclear", "proton", "science" })] + [FontAwesomeSearchTerms(new[] { "atheism", "atheist", "atom", "atom symbol", "chemistry", "electron", "ion", "isotope", "neutron", "nuclear", "proton", "science" })] [FontAwesomeCategoriesAttribute(new[] { "Education", "Energy", "Religion", "Science", "Science Fiction", "Spinners" })] Atom = 0xF5D2, @@ -626,14 +619,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "award" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "award", "guarantee", "honor", "praise", "prize", "recognition", "ribbon", "trophy", "warranty" })] + [FontAwesomeSearchTerms(new[] { "award", "honor", "praise", "prize", "recognition", "ribbon", "trophy" })] [FontAwesomeCategoriesAttribute(new[] { "Education", "Political" })] Award = 0xF559, /// <summary> /// The Font Awesome "baby" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "baby", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "baby", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Childhood", "Humanitarian", "Users + People" })] Baby = 0xF77C, @@ -675,7 +668,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "bacterium" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "bacterium", "antibiotic", "antibody", "covid-19", "germ", "health", "organism", "sick" })] + [FontAwesomeSearchTerms(new[] { "bacterium", "antibiotic", "antibody", "covid-19", "health", "organism", "sick" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Medical + Health" })] Bacterium = 0xE05A, @@ -717,14 +710,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "ban" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "404", "abort", "ban", "block", "cancel", "circle", "delete", "deny", "disabled", "entry", "failed", "forbidden", "hide", "no", "not", "not found", "prohibit", "prohibited", "remove", "slash", "stop", "trash" })] + [FontAwesomeSearchTerms(new[] { "abort", "ban", "block", "cancel", "delete", "entry", "forbidden", "hide", "no", "not", "prohibit", "prohibited", "remove", "stop", "trash" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Security" })] Ban = 0xF05E, /// <summary> /// The Font Awesome "bandage" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "adhesive bandage", "bandage", "boo boo", "first aid", "modify", "ouch" })] + [FontAwesomeSearchTerms(new[] { "adhesive bandage", "bandage", "boo boo", "first aid", "ouch" })] [FontAwesomeCategoriesAttribute(new[] { "Editing", "Medical + Health" })] BandAid = 0xF462, @@ -822,7 +815,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "bed" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "bed", "hospital", "hotel", "lodging", "mattress", "patient", "person in bed", "rest", "sleep", "travel", "uer" })] + [FontAwesomeSearchTerms(new[] { "bed", "hospital", "hotel", "lodging", "mattress", "patient", "person in bed", "rest", "sleep", "travel" })] [FontAwesomeCategoriesAttribute(new[] { "Household", "Humanitarian", "Maps", "Travel + Hotel", "Users + People" })] Bed = 0xF236, @@ -836,7 +829,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "bell" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "alarm", "alert", "bel", "bell", "chime", "notification", "reminder", "request" })] + [FontAwesomeSearchTerms(new[] { "alarm", "alert", "bel", "bell", "chime", "notification", "reminder" })] [FontAwesomeCategoriesAttribute(new[] { "Alert", "Education", "Household", "Maps", "Shopping", "Social", "Time" })] Bell = 0xF0F3, @@ -871,14 +864,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "person-biking" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person biking", "bicycle", "bike", "biking", "cyclist", "pedal", "person biking", "summer", "uer", "wheel" })] + [FontAwesomeSearchTerms(new[] { "person biking", "bicycle", "bike", "biking", "cyclist", "pedal", "person biking", "summer", "wheel" })] [FontAwesomeCategoriesAttribute(new[] { "Childhood", "Sports + Fitness", "Users + People" })] Biking = 0xF84A, /// <summary> /// The Font Awesome "binoculars" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "binoculars", "glasses", "inspection", "magnifier", "magnify", "scenic", "spyglass", "view" })] + [FontAwesomeSearchTerms(new[] { "binoculars", "glasses", "magnify", "scenic", "spyglass", "view" })] [FontAwesomeCategoriesAttribute(new[] { "Astronomy", "Camping", "Maps", "Nature" })] Binoculars = 0xF1E5, @@ -920,7 +913,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "person-walking-with-cane" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person walking with cane", "blind", "cane", "follow", "uer" })] + [FontAwesomeSearchTerms(new[] { "person walking with cane", "blind", "cane" })] [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Maps", "Users + People" })] Blind = 0xF29D, @@ -976,14 +969,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "book" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "book", "cover", "decorated", "diary", "documentation", "journal", "knowledge", "library", "notebook", "notebook with decorative cover", "read", "research", "scholar" })] + [FontAwesomeSearchTerms(new[] { "book", "cover", "decorated", "diary", "documentation", "journal", "library", "notebook", "notebook with decorative cover", "read", "research" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Maps", "Writing" })] Book = 0xF02D, /// <summary> /// The Font Awesome "book-bookmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "book bookmark", "knowledge", "library", "research" })] + [FontAwesomeSearchTerms(new[] { "book bookmark", "library", "research" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Writing" })] BookBookmark = 0xE0BB, @@ -1011,7 +1004,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "book-open" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "book open", "book", "book", "flyer", "knowledge", "library", "notebook", "open", "open book", "pamphlet", "reading", "research" })] + [FontAwesomeSearchTerms(new[] { "book open", "book", "book", "flyer", "library", "notebook", "open", "open book", "pamphlet", "reading", "research" })] [FontAwesomeCategoriesAttribute(new[] { "Education" })] BookOpen = 0xF518, @@ -1137,7 +1130,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "brain" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "brain", "cerebellum", "gray matter", "intellect", "intelligent", "knowledge", "medulla oblongata", "mind", "noodle", "scholar", "wit" })] + [FontAwesomeSearchTerms(new[] { "brain", "cerebellum", "gray matter", "intellect", "intelligent", "medulla oblongata", "mind", "noodle", "wit" })] [FontAwesomeCategoriesAttribute(new[] { "Medical + Health", "Science" })] Brain = 0xF5DC, @@ -1165,28 +1158,28 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "bridge-circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "bridge circle check", "bridge", "enable", "not affected", "ok", "okay", "road", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "bridge circle check", "bridge", "not affected", "ok", "okay", "road" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics" })] BridgeCircleCheck = 0xE4C9, /// <summary> /// The Font Awesome "bridge-circle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "bridge circle exclamation", "affected", "bridge", "failed", "road" })] + [FontAwesomeSearchTerms(new[] { "bridge circle exclamation", "affected", "bridge", "road" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics" })] BridgeCircleExclamation = 0xE4CA, /// <summary> /// The Font Awesome "bridge-circle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "bridge circle xmark", "bridge", "destroy", "road", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "bridge circle xmark", "bridge", "destroy", "road" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics" })] BridgeCircleXmark = 0xE4CB, /// <summary> /// The Font Awesome "bridge-lock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "bridge lock", "bridge", "closed", "lockdown", "padlock", "privacy", "quarantine", "road" })] + [FontAwesomeSearchTerms(new[] { "bridge lock", "bridge", "closed", "lockdown", "quarantine", "road" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics" })] BridgeLock = 0xE4CC, @@ -1200,7 +1193,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "briefcase" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "bag", "briefcas", "briefcase", "business", "luggage", "offer", "office", "portfolio", "work" })] + [FontAwesomeSearchTerms(new[] { "bag", "briefcas", "briefcase", "business", "luggage", "office", "work" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Maps", "Travel + Hotel" })] Briefcase = 0xF0B1, @@ -1214,7 +1207,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "tower-broadcast" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "tower broadcast", "airwaves", "antenna", "communication", "emergency", "radio", "reception", "signal", "waves" })] + [FontAwesomeSearchTerms(new[] { "tower broadcast", "airwaves", "antenna", "communication", "emergency", "radio", "reception", "waves" })] [FontAwesomeCategoriesAttribute(new[] { "Connectivity", "Energy", "Film + Video", "Humanitarian" })] BroadcastTower = 0xF519, @@ -1228,7 +1221,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "brush" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "brush", "art", "bristles", "color", "handle", "maintenance", "modify", "paint" })] + [FontAwesomeSearchTerms(new[] { "brush", "art", "bristles", "color", "handle", "paint" })] [FontAwesomeCategoriesAttribute(new[] { "Construction", "Design", "Editing" })] Brush = 0xF55D, @@ -1256,7 +1249,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "bug-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "bug slash", "beetle", "disabled", "fix", "glitch", "insect", "optimize", "repair", "report", "warning" })] + [FontAwesomeSearchTerms(new[] { "bug slash", "beetle", "fix", "glitch", "insect", "optimize", "repair", "report", "warning" })] [FontAwesomeCategoriesAttribute(new[] { "Coding", "Security" })] BugSlash = 0xE490, @@ -1277,42 +1270,42 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "building-circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "building circle check", "building", "city", "enable", "not affected", "office", "ok", "okay", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "building circle check", "building", "city", "not affected", "office", "ok", "okay" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian" })] BuildingCircleCheck = 0xE4D2, /// <summary> /// The Font Awesome "building-circle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "building circle exclamation", "affected", "building", "city", "failed", "office" })] + [FontAwesomeSearchTerms(new[] { "building circle exclamation", "affected", "building", "city", "office" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian" })] BuildingCircleExclamation = 0xE4D3, /// <summary> /// The Font Awesome "building-circle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "building circle xmark", "building", "city", "destroy", "office", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "building circle xmark", "building", "city", "destroy", "office" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian" })] BuildingCircleXmark = 0xE4D4, /// <summary> /// The Font Awesome "building-flag" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "building flag", "building", "city", "diplomat", "embassy", "flag", "headquarters", "united nations" })] + [FontAwesomeSearchTerms(new[] { "building flag", " city", "building", "diplomat", "embassy", "flag", "headquarters", "united nations" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian", "Political" })] BuildingFlag = 0xE4D5, /// <summary> /// The Font Awesome "building-lock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "building lock", "building", "city", "closed", "lock", "lockdown", "padlock", "privacy", "quarantine", "secure" })] + [FontAwesomeSearchTerms(new[] { "building lock", "building", "city", "closed", "lock", "lockdown", "quarantine", "secure" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian", "Security" })] BuildingLock = 0xE4D6, /// <summary> /// The Font Awesome "building-ngo" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "building ngo", "building", "city", "non governmental organization", "office" })] + [FontAwesomeSearchTerms(new[] { "building ngo", " city", "building", "non governmental organization", "office" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian" })] BuildingNgo = 0xE4D7, @@ -1333,7 +1326,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "building-user" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "building user", "apartment", "building", "city", "employee", "uer" })] + [FontAwesomeSearchTerms(new[] { "building user", "apartment", "building", "city" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian" })] BuildingUser = 0xE4DA, @@ -1347,7 +1340,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "bullhorn" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "bullhorn", "bullhorn", "announcement", "broadcast", "loud", "louder", "loudspeaker", "megaphone", "public address", "request", "share" })] + [FontAwesomeSearchTerms(new[] { "bullhorn", "bullhorn", "announcement", "broadcast", "loud", "louder", "loudspeaker", "megaphone", "public address", "share" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Communication", "Marketing", "Political", "Shopping" })] Bullhorn = 0xF0A1, @@ -1389,17 +1382,10 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "business-time" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "business time", "alarm", "briefcase", "business socks", "clock", "flight of the conchords", "portfolio", "reminder", "wednesday" })] + [FontAwesomeSearchTerms(new[] { "business time", "alarm", "briefcase", "business socks", "clock", "flight of the conchords", "reminder", "wednesday" })] [FontAwesomeCategoriesAttribute(new[] { "Business" })] BusinessTime = 0xF64A, - /// <summary> - /// The Font Awesome "bus-side" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "bus side", "bus", "public transportation", "transportation", "travel", "vehicle" })] - [FontAwesomeCategoriesAttribute(new[] { "Automotive", "Humanitarian", "Logistics", "Transportation", "Travel + Hotel" })] - BusSide = 0xE81D, - /// <summary> /// The Font Awesome "calculator" icon unicode character. /// </summary> @@ -1424,7 +1410,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "calendar-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "calendar check", "accept", "agree", "appointment", "confirm", "correct", "date", "day", "done", "enable", "event", "month", "ok", "schedule", "select", "success", "tick", "time", "todo", "validate", "warranty", "when", "working", "year" })] + [FontAwesomeSearchTerms(new[] { "calendar check", "accept", "agree", "appointment", "confirm", "correct", "date", "day", "done", "event", "month", "ok", "schedule", "select", "success", "tick", "time", "todo", "when", "year" })] [FontAwesomeCategoriesAttribute(new[] { "Time" })] CalendarCheck = 0xF274, @@ -1452,7 +1438,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "calendar-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "calendar xmark", "archive", "calendar", "date", "day", "delete", "event", "month", "remove", "schedule", "time", "uncheck", "when", "x", "year" })] + [FontAwesomeSearchTerms(new[] { "calendar xmark", "archive", "calendar", "date", "day", "delete", "event", "month", "remove", "schedule", "time", "when", "x", "year" })] [FontAwesomeCategoriesAttribute(new[] { "Time" })] CalendarTimes = 0xF273, @@ -1466,21 +1452,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "camera" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "camera", "image", "img", "lens", "photo", "picture", "record", "shutter", "video" })] + [FontAwesomeSearchTerms(new[] { "camera", "image", "lens", "photo", "picture", "record", "shutter", "video" })] [FontAwesomeCategoriesAttribute(new[] { "Devices + Hardware", "Photos + Images", "Shopping", "Social" })] Camera = 0xF030, /// <summary> /// The Font Awesome "camera-retro" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "camera retro", "camera", "image", "img", "lens", "photo", "picture", "record", "shutter", "video" })] + [FontAwesomeSearchTerms(new[] { "camera retro", "camera", "image", "lens", "photo", "picture", "record", "shutter", "video" })] [FontAwesomeCategoriesAttribute(new[] { "Devices + Hardware", "Photos + Images", "Shopping" })] CameraRetro = 0xF083, /// <summary> /// The Font Awesome "camera-rotate" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "camera rotate", "flip", "front-facing", "img", "photo", "selfie" })] + [FontAwesomeSearchTerms(new[] { "camera rotate", "flip", "front-facing", "photo", "selfie" })] [FontAwesomeCategoriesAttribute(new[] { "Photos + Images" })] CameraRotate = 0xE0D8, @@ -1571,7 +1557,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "square-caret-down" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "square caret down", "arrow", "caret-square-o-down", "dropdown", "expand", "insert", "menu", "more", "triangle" })] + [FontAwesomeSearchTerms(new[] { "square caret down", "arrow", "caret-square-o-down", "dropdown", "expand", "menu", "more", "triangle" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] CaretSquareDown = 0xF150, @@ -1592,14 +1578,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "square-caret-up" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "square caret up", "arrow", "caret-square-o-up", "collapse", "triangle", "upgrade", "upload" })] + [FontAwesomeSearchTerms(new[] { "square caret up", "arrow", "caret-square-o-up", "collapse", "triangle", "upload" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] CaretSquareUp = 0xF151, /// <summary> /// The Font Awesome "caret-up" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "caret up", "arrow", "collapse", "triangle", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "caret up", "arrow", "collapse", "triangle" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] CaretUp = 0xF0D8, @@ -1627,7 +1613,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "cart-arrow-down" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "cart arrow down", "download", "insert", "save", "shopping" })] + [FontAwesomeSearchTerms(new[] { "cart arrow down", "download", "save", "shopping" })] [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] CartArrowDown = 0xF218, @@ -1676,7 +1662,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "certificate" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "certificate", "badge", "guarantee", "star", "verified" })] + [FontAwesomeSearchTerms(new[] { "certificate", "badge", "star", "verified" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Shapes", "Shopping", "Spinners" })] Certificate = 0xF0A3, @@ -1697,98 +1683,91 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "chalkboard-user" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "chalkboard user", "blackboard", "instructor", "learning", "professor", "school", "uer", "whiteboard", "writing" })] + [FontAwesomeSearchTerms(new[] { "chalkboard user", "blackboard", "instructor", "learning", "professor", "school", "whiteboard", "writing" })] [FontAwesomeCategoriesAttribute(new[] { "Education", "Users + People" })] ChalkboardTeacher = 0xF51C, /// <summary> /// The Font Awesome "charging-station" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "charging station", "car charger", "charge", "charging", "electric", "ev", "tesla", "vehicle" })] + [FontAwesomeSearchTerms(new[] { "charging station", "electric", "ev", "tesla", "vehicle" })] [FontAwesomeCategoriesAttribute(new[] { "Automotive", "Energy" })] ChargingStation = 0xF5E7, /// <summary> /// The Font Awesome "chart-area" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "chart area", "analytics", "area", "chart", "graph", "performance", "revenue", "statistics" })] + [FontAwesomeSearchTerms(new[] { "chart area", "analytics", "area", "chart", "graph" })] [FontAwesomeCategoriesAttribute(new[] { "Charts + Diagrams" })] ChartArea = 0xF1FE, /// <summary> /// The Font Awesome "chart-bar" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "chart bar", "analytics", "bar", "chart", "graph", "performance", "statistics" })] + [FontAwesomeSearchTerms(new[] { "chart bar", "analytics", "bar", "chart", "graph" })] [FontAwesomeCategoriesAttribute(new[] { "Charts + Diagrams" })] ChartBar = 0xF080, /// <summary> /// The Font Awesome "chart-column" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "chart column", "bar", "bar chart", "chart", "graph", "performance", "revenue", "statistics", "track", "trend" })] + [FontAwesomeSearchTerms(new[] { "chart column", "bar", "bar chart", "chart", "graph", "track", "trend" })] [FontAwesomeCategoriesAttribute(new[] { "Charts + Diagrams" })] ChartColumn = 0xE0E3, - /// <summary> - /// The Font Awesome "chart-diagram" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "chart diagram", "algorithm", "analytics", "flow", "graph" })] - [FontAwesomeCategoriesAttribute(new[] { "Charts + Diagrams", "Coding" })] - ChartDiagram = 0xE695, - /// <summary> /// The Font Awesome "chart-gantt" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "chart gantt", "chart", "graph", "performance", "statistics", "track", "trend" })] + [FontAwesomeSearchTerms(new[] { "chart gantt", "chart", "graph", "track", "trend" })] [FontAwesomeCategoriesAttribute(new[] { "Charts + Diagrams" })] ChartGantt = 0xE0E4, /// <summary> /// The Font Awesome "chart-line" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "chart line", "activity", "analytics", "chart", "dashboard", "gain", "graph", "increase", "line", "performance", "revenue", "statistics" })] + [FontAwesomeSearchTerms(new[] { "chart line", "activity", "analytics", "chart", "dashboard", "gain", "graph", "increase", "line" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Charts + Diagrams", "Money" })] ChartLine = 0xF201, /// <summary> /// The Font Awesome "chart-pie" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "chart pie", "analytics", "chart", "diagram", "graph", "performance", "pie", "revenue", "statistics" })] + [FontAwesomeSearchTerms(new[] { "chart pie", "analytics", "chart", "diagram", "graph", "pie" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Charts + Diagrams", "Money" })] ChartPie = 0xF200, /// <summary> /// The Font Awesome "chart-simple" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "chart simple", "analytics", "bar", "chart", "column", "graph", "performance", "revenue", "row", "statistics", "trend" })] + [FontAwesomeSearchTerms(new[] { "chart simple", "analytics", "bar", "chart", "column", "graph", "row", "trend" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Charts + Diagrams", "Editing", "Logistics", "Marketing" })] ChartSimple = 0xE473, /// <summary> /// The Font Awesome "check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "check mark", "accept", "agree", "check", "check mark", "checkmark", "confirm", "correct", "coupon", "done", "enable", "mark", "notice", "notification", "notify", "ok", "select", "success", "tick", "todo", "true", "validate", "working", "yes", "✓" })] + [FontAwesomeSearchTerms(new[] { "check mark", "accept", "agree", "check", "check mark", "checkmark", "confirm", "correct", "done", "mark", "notice", "notification", "notify", "ok", "select", "success", "tick", "todo", "yes", "✓" })] [FontAwesomeCategoriesAttribute(new[] { "Editing", "Punctuation + Symbols", "Text Formatting" })] Check = 0xF00C, /// <summary> /// The Font Awesome "circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "circle check", "accept", "affected", "agree", "clear", "confirm", "correct", "coupon", "done", "enable", "ok", "select", "success", "tick", "todo", "validate", "working", "yes" })] + [FontAwesomeSearchTerms(new[] { "circle check", "accept", "affected", "agree", "clear", "confirm", "correct", "done", "ok", "select", "success", "tick", "todo", "yes" })] [FontAwesomeCategoriesAttribute(new[] { "Editing", "Text Formatting", "Toggle" })] CheckCircle = 0xF058, /// <summary> /// The Font Awesome "check-double" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "check double", "accept", "agree", "checkmark", "confirm", "correct", "coupon", "done", "enable", "notice", "notification", "notify", "ok", "select", "select all", "success", "tick", "todo", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "check double", "accept", "agree", "checkmark", "confirm", "correct", "done", "notice", "notification", "notify", "ok", "select", "success", "tick", "todo" })] [FontAwesomeCategoriesAttribute(new[] { "Editing", "Political", "Punctuation + Symbols", "Text Formatting" })] CheckDouble = 0xF560, /// <summary> /// The Font Awesome "square-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "square check", "accept", "agree", "box", "button", "check", "check box with check", "check mark button", "checkmark", "confirm", "correct", "coupon", "done", "enable", "mark", "ok", "select", "success", "tick", "todo", "validate", "working", "yes", "✓" })] + [FontAwesomeSearchTerms(new[] { "square check", "accept", "agree", "box", "button", "check", "check box with check", "check mark button", "checkmark", "confirm", "correct", "done", "mark", "ok", "select", "success", "tick", "todo", "yes", "✓" })] [FontAwesomeCategoriesAttribute(new[] { "Editing", "Text Formatting" })] CheckSquare = 0xF14A, @@ -1879,14 +1858,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "circle-chevron-up" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "circle chevron up", "arrow", "collapse", "upgrade", "upload" })] + [FontAwesomeSearchTerms(new[] { "circle chevron up", "arrow", "collapse", "upload" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] ChevronCircleUp = 0xF139, /// <summary> /// The Font Awesome "chevron-down" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "chevron down", "arrow", "download", "expand", "insert" })] + [FontAwesomeSearchTerms(new[] { "chevron down", "arrow", "download", "expand" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] ChevronDown = 0xF078, @@ -1907,14 +1886,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "chevron-up" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "chevron up", "arrow", "collapse", "upgrade", "upload" })] + [FontAwesomeSearchTerms(new[] { "chevron up", "arrow", "collapse", "upload" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] ChevronUp = 0xF077, /// <summary> /// The Font Awesome "child" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "child", "boy", "girl", "kid", "toddler", "uer", "young", "youth" })] + [FontAwesomeSearchTerms(new[] { "child", "boy", "girl", "kid", "toddler", "young", "youth" })] [FontAwesomeCategoriesAttribute(new[] { "Childhood", "Users + People" })] Child = 0xF1AE, @@ -1928,21 +1907,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "child-dress" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "child dress", "boy", "girl", "kid", "toddler", "uer", "young", "youth" })] + [FontAwesomeSearchTerms(new[] { "child dress", "boy", "girl", "kid", "toddler", "young", "youth" })] [FontAwesomeCategoriesAttribute(new[] { "Childhood", "Users + People" })] ChildDress = 0xE59C, /// <summary> /// The Font Awesome "child-reaching" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "child reaching", "boy", "girl", "kid", "toddler", "uer", "young", "youth" })] + [FontAwesomeSearchTerms(new[] { "child reaching", "boy", "girl", "kid", "toddler", "young", "youth" })] [FontAwesomeCategoriesAttribute(new[] { "Childhood", "Users + People" })] ChildReaching = 0xE59D, /// <summary> /// The Font Awesome "children" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "children", "boy", "child", "girl", "kid", "kids", "together", "uer", "young", "youth" })] + [FontAwesomeSearchTerms(new[] { "children", "boy", "child", "girl", "kid", "kids", "young", "youth" })] [FontAwesomeCategoriesAttribute(new[] { "Childhood", "Humanitarian", "Users + People" })] Children = 0xE4E1, @@ -1998,49 +1977,49 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "clipboard" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "clipboard", "copy", "notepad", "notes", "paste", "record" })] + [FontAwesomeSearchTerms(new[] { "clipboar", "clipboard", "copy", "notes", "paste", "record" })] [FontAwesomeCategoriesAttribute(new[] { "Business" })] Clipboard = 0xF328, /// <summary> /// The Font Awesome "clipboard-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "clipboard check", "accept", "agree", "confirm", "coupon", "done", "enable", "ok", "select", "success", "tick", "todo", "validate", "working", "yes" })] - [FontAwesomeCategoriesAttribute(new[] { "Business", "Logistics", "Science" })] + [FontAwesomeSearchTerms(new[] { "clipboard check", "accept", "agree", "confirm", "done", "ok", "select", "success", "tick", "todo", "yes" })] + [FontAwesomeCategoriesAttribute(new[] { "Logistics", "Science" })] ClipboardCheck = 0xF46C, /// <summary> /// The Font Awesome "clipboard-list" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "clipboard list", "cheatsheet", "checklist", "completed", "done", "finished", "intinerary", "ol", "schedule", "summary", "survey", "tick", "todo", "ul", "wishlist" })] + [FontAwesomeSearchTerms(new[] { "clipboard list", "checklist", "completed", "done", "finished", "intinerary", "ol", "schedule", "tick", "todo", "ul" })] [FontAwesomeCategoriesAttribute(new[] { "Logistics" })] ClipboardList = 0xF46D, /// <summary> /// The Font Awesome "clipboard-question" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "clipboard question", "assistance", "faq", "interview", "query", "question" })] + [FontAwesomeSearchTerms(new[] { "clipboard question", "assistance", "interview", "query", "question" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Humanitarian", "Logistics" })] ClipboardQuestion = 0xE4E3, /// <summary> /// The Font Awesome "clipboard-user" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "clipboard user", "attendance", "employee", "record", "roster", "staff", "uer" })] - [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics", "Medical + Health", "Users + People" })] + [FontAwesomeSearchTerms(new[] { "clipboard user", "attendance", "record", "roster", "staff" })] + [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Medical + Health", "Users + People" })] ClipboardUser = 0xF7F3, /// <summary> /// The Font Awesome "clock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "00", "4", "4:00", "clock", "date", "four", "four o’clock", "hour", "late", "minute", "o'clock", "o’clock", "pending", "schedule", "ticking", "time", "timer", "timestamp", "watch" })] + [FontAwesomeSearchTerms(new[] { "00", "4", "4:00", "clock", "date", "four", "four o’clock", "hour", "late", "minute", "o'clock", "o’clock", "schedule", "ticking", "time", "timer", "timestamp", "watch" })] [FontAwesomeCategoriesAttribute(new[] { "Time" })] Clock = 0xF017, /// <summary> /// The Font Awesome "clone" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "clone", "add", "arrange", "copy", "duplicate", "new", "paste" })] + [FontAwesomeSearchTerms(new[] { "clone", "arrange", "copy", "duplicate", "paste" })] [FontAwesomeCategoriesAttribute(new[] { "Design", "Files", "Photos + Images" })] Clone = 0xF24D, @@ -2133,7 +2112,7 @@ public enum FontAwesomeIcon /// The Font Awesome "cloud-arrow-up" icon unicode character. /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0xF0EE. /// </summary> - [FontAwesomeSearchTerms(new[] { "cloud arrow up", "import", "save", "upgrade", "upload" })] + [FontAwesomeSearchTerms(new[] { "cloud arrow up", "import", "save", "upload" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Connectivity" })] CloudUploadAlt = 0xF382, @@ -2154,14 +2133,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "code" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "brackets", "code", "development", "html", "mysql", "sql" })] + [FontAwesomeSearchTerms(new[] { "brackets", "code", "development", "html" })] [FontAwesomeCategoriesAttribute(new[] { "Coding" })] Code = 0xF121, /// <summary> /// The Font Awesome "code-branch" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "code branch", "branch", "git", "github", "mysql", "rebase", "sql", "svn", "vcs", "version" })] + [FontAwesomeSearchTerms(new[] { "code branch", "branch", "git", "github", "rebase", "svn", "vcs", "version" })] [FontAwesomeCategoriesAttribute(new[] { "Coding" })] CodeBranch = 0xF126, @@ -2210,21 +2189,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "gear" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "cog", "cogwheel", "configuration", "gear", "mechanical", "modify", "settings", "sprocket", "tool", "wheel" })] + [FontAwesomeSearchTerms(new[] { "cog", "cogwheel", "gear", "mechanical", "settings", "sprocket", "tool", "wheel" })] [FontAwesomeCategoriesAttribute(new[] { "Coding", "Editing", "Spinners" })] Cog = 0xF013, /// <summary> /// The Font Awesome "gears" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "configuration", "gears", "mechanical", "modify", "settings", "sprocket", "wheel" })] + [FontAwesomeSearchTerms(new[] { "gears", "mechanical", "settings", "sprocket", "wheel" })] [FontAwesomeCategoriesAttribute(new[] { "Coding", "Logistics" })] Cogs = 0xF085, /// <summary> /// The Font Awesome "coins" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "coins", "currency", "dime", "financial", "gold", "money", "penny", "premium" })] + [FontAwesomeSearchTerms(new[] { "coins", "currency", "dime", "financial", "gold", "money", "penny" })] [FontAwesomeCategoriesAttribute(new[] { "Money" })] Coins = 0xF51E, @@ -2238,70 +2217,63 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "table-columns" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "table columns", "browser", "category", "dashboard", "organize", "panes", "split" })] + [FontAwesomeSearchTerms(new[] { "table columns", "browser", "dashboard", "organize", "panes", "split" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Text Formatting" })] Columns = 0xF0DB, /// <summary> /// The Font Awesome "comment" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "comment", "right speech bubble", "answer", "bubble", "chat", "commenting", "conversation", "conversation", "discussion", "feedback", "message", "note", "notification", "sms", "speech", "talk", "talking", "texting" })] + [FontAwesomeSearchTerms(new[] { "comment", "right speech bubble", "bubble", "chat", "commenting", "conversation", "feedback", "message", "note", "notification", "sms", "speech", "texting" })] [FontAwesomeCategoriesAttribute(new[] { "Communication", "Shapes", "Social" })] Comment = 0xF075, /// <summary> /// The Font Awesome "message" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "answer", "bubble", "chat", "commenting", "conversation", "conversation", "discussion", "feedback", "message", "note", "notification", "sms", "speech", "talk", "talking", "texting" })] + [FontAwesomeSearchTerms(new[] { "bubble", "chat", "commenting", "conversation", "feedback", "message", "note", "notification", "sms", "speech", "texting" })] [FontAwesomeCategoriesAttribute(new[] { "Communication", "Social" })] CommentAlt = 0xF27A, /// <summary> /// The Font Awesome "comment-dollar" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "comment dollar", "answer", "bubble", "chat", "commenting", "conversation", "feedback", "message", "money", "note", "notification", "pay", "salary", "sms", "speech", "spend", "texting", "transfer" })] + [FontAwesomeSearchTerms(new[] { "comment dollar", "bubble", "chat", "commenting", "conversation", "feedback", "message", "money", "note", "notification", "pay", "sms", "speech", "spend", "texting", "transfer" })] [FontAwesomeCategoriesAttribute(new[] { "Marketing", "Money" })] CommentDollar = 0xF651, /// <summary> /// The Font Awesome "comment-dots" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "comment dots", "answer", "balloon", "bubble", "chat", "comic", "commenting", "conversation", "dialog", "feedback", "message", "more", "note", "notification", "reply", "request", "sms", "speech", "speech balloon", "texting" })] + [FontAwesomeSearchTerms(new[] { "comment dots", "balloon", "bubble", "chat", "comic", "commenting", "conversation", "dialog", "feedback", "message", "more", "note", "notification", "reply", "sms", "speech", "speech balloon", "texting" })] [FontAwesomeCategoriesAttribute(new[] { "Communication" })] CommentDots = 0xF4AD, /// <summary> /// The Font Awesome "comment-medical" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "comment medical", "advice", "answer", "bubble", "chat", "commenting", "conversation", "diagnose", "feedback", "message", "note", "notification", "prescription", "sms", "speech", "texting" })] + [FontAwesomeSearchTerms(new[] { "comment medical", "advice", "bubble", "chat", "commenting", "conversation", "diagnose", "feedback", "message", "note", "notification", "prescription", "sms", "speech", "texting" })] [FontAwesomeCategoriesAttribute(new[] { "Communication", "Medical + Health" })] CommentMedical = 0xF7F5, - /// <summary> - /// The Font Awesome "comment-nodes" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "comment nodes", "ai", "artificial intelligence", "cluster", "language", "model", "network", "neuronal" })] - [FontAwesomeCategoriesAttribute(new[] { "Coding", "Communication" })] - CommentNodes = 0xE696, - /// <summary> /// The Font Awesome "comments" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "comments", "two speech bubbles", "answer", "bubble", "chat", "commenting", "conversation", "conversation", "discussion", "feedback", "message", "note", "notification", "sms", "speech", "talk", "talking", "texting" })] + [FontAwesomeSearchTerms(new[] { "comments", "two speech bubbles", "bubble", "chat", "commenting", "conversation", "feedback", "message", "note", "notification", "sms", "speech", "texting" })] [FontAwesomeCategoriesAttribute(new[] { "Communication" })] Comments = 0xF086, /// <summary> /// The Font Awesome "comments-dollar" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "comments dollar", "answer", "bubble", "chat", "commenting", "conversation", "feedback", "message", "money", "note", "notification", "pay", "salary", "sms", "speech", "spend", "texting", "transfer" })] + [FontAwesomeSearchTerms(new[] { "comments dollar", "bubble", "chat", "commenting", "conversation", "feedback", "message", "money", "note", "notification", "pay", "sms", "speech", "spend", "texting", "transfer" })] [FontAwesomeCategoriesAttribute(new[] { "Marketing", "Money" })] CommentsDollar = 0xF653, /// <summary> /// The Font Awesome "comment-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "comment slash", "answer", "bubble", "cancel", "chat", "commenting", "conversation", "disabled", "feedback", "message", "mute", "note", "notification", "quiet", "sms", "speech", "texting" })] + [FontAwesomeSearchTerms(new[] { "comment slash", "bubble", "cancel", "chat", "commenting", "conversation", "feedback", "message", "mute", "note", "notification", "quiet", "sms", "speech", "texting" })] [FontAwesomeCategoriesAttribute(new[] { "Communication" })] CommentSlash = 0xF4B3, @@ -2329,7 +2301,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "down-left-and-up-right-to-center" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "down left and up right to center", "collapse", "fullscreen", "minimize", "move", "resize", "scale", "shrink", "size", "smaller" })] + [FontAwesomeSearchTerms(new[] { "down left and up right to center", "collapse", "fullscreen", "minimize", "move", "resize", "shrink", "smaller" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback" })] CompressAlt = 0xF422, @@ -2350,7 +2322,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "bell-concierge" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "bell concierge", "attention", "bell", "bellhop", "bellhop bell", "hotel", "receptionist", "request", "service", "support" })] + [FontAwesomeSearchTerms(new[] { "bell concierge", "attention", "bell", "bellhop", "bellhop bell", "hotel", "receptionist", "service", "support" })] [FontAwesomeCategoriesAttribute(new[] { "Travel + Hotel" })] ConciergeBell = 0xF562, @@ -2406,14 +2378,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "crop" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "crop", "design", "frame", "mask", "modify", "resize", "shrink" })] + [FontAwesomeSearchTerms(new[] { "crop", "design", "frame", "mask", "resize", "shrink" })] [FontAwesomeCategoriesAttribute(new[] { "Design", "Editing" })] Crop = 0xF125, /// <summary> /// The Font Awesome "crop-simple" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "crop simple", "design", "frame", "mask", "modify", "resize", "shrink" })] + [FontAwesomeSearchTerms(new[] { "crop simple", "design", "frame", "mask", "resize", "shrink" })] [FontAwesomeCategoriesAttribute(new[] { "Design", "Editing" })] CropAlt = 0xF565, @@ -2441,7 +2413,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "crown" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "award", "clothing", "crown", "favorite", "king", "queen", "royal", "tiara", "vip" })] + [FontAwesomeSearchTerms(new[] { "award", "clothing", "crown", "favorite", "king", "queen", "royal", "tiara" })] [FontAwesomeCategoriesAttribute(new[] { "Shapes" })] Crown = 0xF521, @@ -2483,14 +2455,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "scissors" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "black safety scissors", "white scissors", "clip", "cutting", "equipment", "modify", "scissors", "snip", "tool" })] + [FontAwesomeSearchTerms(new[] { "black safety scissors", "white scissors", "clip", "cutting", "scissors", "snip", "tool" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Design", "Editing", "Files" })] Cut = 0xF0C4, /// <summary> /// The Font Awesome "database" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "database", "computer", "development", "directory", "memory", "mysql", "sql", "storage" })] + [FontAwesomeSearchTerms(new[] { "database", "computer", "development", "directory", "memory", "storage" })] [FontAwesomeCategoriesAttribute(new[] { "Devices + Hardware" })] Database = 0xF1C0, @@ -2498,7 +2470,7 @@ public enum FontAwesomeIcon /// The Font Awesome "ear-deaf" icon unicode character. /// </summary> [FontAwesomeSearchTerms(new[] { "ear deaf", "ear", "hearing", "sign language" })] - [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Communication" })] + [FontAwesomeCategoriesAttribute(new[] { "Accessibility" })] Deaf = 0xF2A4, /// <summary> @@ -2526,7 +2498,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "person-dots-from-line" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person dots from line", "allergy", "diagnosis", "uer" })] + [FontAwesomeSearchTerms(new[] { "person dots from line", "allergy", "diagnosis" })] [FontAwesomeCategoriesAttribute(new[] { "Medical + Health", "Users + People" })] Diagnoses = 0xF470, @@ -2554,7 +2526,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "diamond" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "diamond", "ace", "card", "cards", "diamond suit", "game", "gem", "gemstone", "poker", "suit" })] + [FontAwesomeSearchTerms(new[] { "diamond", "card", "cards", "diamond suit", "game", "gem", "gemstone", "poker", "suit" })] [FontAwesomeCategoriesAttribute(new[] { "Gaming", "Shapes" })] Diamond = 0xF219, @@ -2681,7 +2653,7 @@ public enum FontAwesomeIcon /// The Font Awesome "dollar-sign" icon unicode character. /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0x24. /// </summary> - [FontAwesomeSearchTerms(new[] { "dollar sign", "dollar sign", "coupon", "currency", "dollar", "heavy dollar sign", "investment", "money", "premium", "revenue", "salary" })] + [FontAwesomeSearchTerms(new[] { "dollar sign", "dollar sign", "currency", "dollar", "heavy dollar sign", "money" })] [FontAwesomeCategoriesAttribute(new[] { "Charity", "Maps", "Money" })] DollarSign = 0xF155, @@ -2702,7 +2674,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "circle-dollar-to-slot" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "circle dollar to slot", "contribute", "generosity", "gift", "give", "premium" })] + [FontAwesomeSearchTerms(new[] { "circle dollar to slot", "contribute", "generosity", "gift", "give" })] [FontAwesomeCategoriesAttribute(new[] { "Charity", "Money", "Political" })] Donate = 0xF4B9, @@ -2716,7 +2688,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "door-closed" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "door closed", "doo", "door", "enter", "exit", "locked", "privacy" })] + [FontAwesomeSearchTerms(new[] { "door closed", "doo", "door", "enter", "exit", "locked" })] [FontAwesomeCategoriesAttribute(new[] { "Household", "Security", "Travel + Hotel" })] DoorClosed = 0xF52A, @@ -2744,7 +2716,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "download" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "download", "export", "hard drive", "insert", "save", "transfer" })] + [FontAwesomeSearchTerms(new[] { "download", "export", "hard drive", "save", "transfer" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Devices + Hardware" })] Download = 0xF019, @@ -2793,7 +2765,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "dumbbell" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "dumbbell", "exercise", "gym", "strength", "weight", "weight-lifting", "workout" })] + [FontAwesomeSearchTerms(new[] { "dumbbell", "exercise", "gym", "strength", "weight", "weight-lifting" })] [FontAwesomeCategoriesAttribute(new[] { "Sports + Fitness", "Travel + Hotel" })] Dumbbell = 0xF44B, @@ -2828,7 +2800,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "pen-to-square" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "pen to square", "edit", "modify", "pen", "pencil", "update", "write" })] + [FontAwesomeSearchTerms(new[] { "pen to square", "edit", "pen", "pencil", "update", "write" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Design", "Editing", "Writing" })] Edit = 0xF044, @@ -2849,56 +2821,56 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "elevator" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "accessibility", "elevator", "hoist", "lift", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "accessibility", "elevator", "hoist", "lift", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Travel + Hotel", "Users + People" })] Elevator = 0xE16D, /// <summary> /// The Font Awesome "ellipsis" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "ellipsis", "dots", "drag", "kebab", "list", "menu", "nav", "navigation", "ol", "pacman", "reorder", "settings", "three dots", "ul" })] + [FontAwesomeSearchTerms(new[] { "ellipsis", "dots", "drag", "kebab", "list", "menu", "nav", "navigation", "ol", "pacman", "reorder", "settings", "ul" })] [FontAwesomeCategoriesAttribute(new[] { "Editing" })] EllipsisH = 0xF141, /// <summary> /// The Font Awesome "ellipsis-vertical" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "ellipsis vertical", "bullet", "dots", "drag", "kebab", "list", "menu", "nav", "navigation", "ol", "reorder", "settings", "three dots", "ul" })] + [FontAwesomeSearchTerms(new[] { "ellipsis vertical", "dots", "drag", "kebab", "list", "menu", "nav", "navigation", "ol", "reorder", "settings", "ul" })] [FontAwesomeCategoriesAttribute(new[] { "Editing" })] EllipsisV = 0xF142, /// <summary> /// The Font Awesome "envelope" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "back of envelope", "e-mail", "email", "envelope", "letter", "mail", "message", "newsletter", "notification", "offer", "support" })] + [FontAwesomeSearchTerms(new[] { "back of envelope", "e-mail", "email", "envelope", "letter", "mail", "message", "notification", "support" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Communication", "Humanitarian", "Social", "Writing" })] Envelope = 0xF0E0, /// <summary> /// The Font Awesome "envelope-circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "envelope circle check", "check", "email", "enable", "envelope", "mail", "not affected", "ok", "okay", "read", "sent", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "envelope circle check", "check", "email", "envelope", "mail", "not affected", "ok", "okay", "read", "sent" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Communication", "Humanitarian" })] EnvelopeCircleCheck = 0xE4E8, /// <summary> /// The Font Awesome "envelope-open" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "envelope open", "e-mail", "email", "letter", "mail", "message", "newsletter", "notification", "offer", "support" })] + [FontAwesomeSearchTerms(new[] { "envelope open", "e-mail", "email", "letter", "mail", "message", "notification", "support" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Communication", "Writing" })] EnvelopeOpen = 0xF2B6, /// <summary> /// The Font Awesome "envelope-open-text" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "envelope open text", "e-mail", "email", "letter", "mail", "message", "newsletter", "notification", "offer", "support" })] + [FontAwesomeSearchTerms(new[] { "envelope open text", "e-mail", "email", "letter", "mail", "message", "notification", "support" })] [FontAwesomeCategoriesAttribute(new[] { "Marketing" })] EnvelopeOpenText = 0xF658, /// <summary> /// The Font Awesome "square-envelope" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "square envelope", "e-mail", "email", "letter", "mail", "message", "notification", "offer", "support" })] + [FontAwesomeSearchTerms(new[] { "square envelope", "e-mail", "email", "letter", "mail", "message", "notification", "support" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Communication" })] EnvelopeSquare = 0xF199, @@ -2942,42 +2914,42 @@ public enum FontAwesomeIcon /// The Font Awesome "exclamation" icon unicode character. /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0x21. /// </summary> - [FontAwesomeSearchTerms(new[] { "!", "exclamation mark", "alert", "attention", "danger", "error", "exclamation", "failed", "important", "mark", "notice", "notification", "notify", "outlined", "problem", "punctuation", "red exclamation mark", "required", "warning", "white exclamation mark" })] + [FontAwesomeSearchTerms(new[] { "!", "exclamation mark", "alert", "danger", "error", "exclamation", "important", "mark", "notice", "notification", "notify", "outlined", "problem", "punctuation", "red exclamation mark", "warning", "white exclamation mark" })] [FontAwesomeCategoriesAttribute(new[] { "Alert", "Punctuation + Symbols" })] Exclamation = 0xF12A, /// <summary> /// The Font Awesome "circle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "circle exclamation", "affect", "alert", "attention", "damage", "danger", "error", "failed", "important", "notice", "notification", "notify", "problem", "required", "warning" })] + [FontAwesomeSearchTerms(new[] { "circle exclamation", "affect", "alert", "damage", "danger", "error", "important", "notice", "notification", "notify", "problem", "warning" })] [FontAwesomeCategoriesAttribute(new[] { "Alert", "Punctuation + Symbols" })] ExclamationCircle = 0xF06A, /// <summary> /// The Font Awesome "triangle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "triangle exclamation", "alert", "attention", "danger", "error", "failed", "important", "notice", "notification", "notify", "problem", "required", "warnin", "warning" })] + [FontAwesomeSearchTerms(new[] { "triangle exclamation", "alert", "danger", "error", "important", "notice", "notification", "notify", "problem", "warnin", "warning" })] [FontAwesomeCategoriesAttribute(new[] { "Alert" })] ExclamationTriangle = 0xF071, /// <summary> /// The Font Awesome "expand" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrows", "bigger", "enlarge", "expand", "fullscreen", "maximize", "resize", "resize", "scale", "size", "viewfinder" })] + [FontAwesomeSearchTerms(new[] { "expand", "bigger", "crop", "enlarge", "focus", "fullscreen", "resize", "viewfinder" })] [FontAwesomeCategoriesAttribute(new[] { "Media Playback" })] Expand = 0xF065, /// <summary> /// The Font Awesome "up-right-and-down-left-from-center" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "up right and down left from center", "arrows", "bigger", "enlarge", "expand", "fullscreen", "maximize", "resize", "resize", "scale", "size" })] + [FontAwesomeSearchTerms(new[] { "up right and down left from center", "arrows", "bigger", "enlarge", "fullscreen", "resize" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback" })] ExpandAlt = 0xF424, /// <summary> /// The Font Awesome "maximize" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrows", "bigger", "enlarge", "expand", "fullscreen", "maximize", "resize", "resize", "scale", "size" })] + [FontAwesomeSearchTerms(new[] { "maximize", "bigger", "enlarge", "fullscreen", "move", "resize" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback" })] ExpandArrowsAlt = 0xF31E, @@ -2991,7 +2963,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "up-right-from-square" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "up right from square", "external-link", "new", "open", "share", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "up right from square", "external-link", "new", "open", "share" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] ExternalLinkAlt = 0xF35D, @@ -3019,7 +2991,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "eye-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "eye slash", "blind", "disabled", "hide", "show", "toggle", "unseen", "views", "visible", "visiblity" })] + [FontAwesomeSearchTerms(new[] { "eye slash", "blind", "hide", "show", "toggle", "unseen", "views", "visible", "visiblity" })] [FontAwesomeCategoriesAttribute(new[] { "Design", "Editing", "Maps", "Photos + Images", "Security" })] EyeSlash = 0xF070, @@ -3033,14 +3005,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "backward-fast" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "backward fast", "arrow", "beginning", "first", "last track button", "previous", "previous scene", "previous track", "quick", "rewind", "start", "triangle" })] + [FontAwesomeSearchTerms(new[] { "backward fast", "arrow", "beginning", "first", "last track button", "previous", "previous scene", "previous track", "rewind", "start", "triangle" })] [FontAwesomeCategoriesAttribute(new[] { "Media Playback" })] FastBackward = 0xF049, /// <summary> /// The Font Awesome "forward-fast" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "forward fast", "arrow", "end", "last", "next", "next scene", "next track", "next track button", "quick", "triangle" })] + [FontAwesomeSearchTerms(new[] { "forward fast", "arrow", "end", "last", "next", "next scene", "next track", "next track button", "triangle" })] [FontAwesomeCategoriesAttribute(new[] { "Media Playback" })] FastForward = 0xF050, @@ -3082,7 +3054,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "person-dress" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person dress", "man", "skirt", "uer", "woman" })] + [FontAwesomeSearchTerms(new[] { "person dress", "man", "skirt", "woman" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] Female = 0xF182, @@ -3103,7 +3075,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "file" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "file", "empty document", "cv", "document", "new", "page", "page facing up", "pdf", "resume" })] + [FontAwesomeSearchTerms(new[] { "file", "empty document", "document", "new", "page", "page facing up", "pdf", "resume" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Coding", "Files", "Humanitarian", "Shapes", "Writing" })] File = 0xF15B, @@ -3131,14 +3103,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "file-circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "file circle check", "document", "enable", "file", "not affected", "ok", "okay", "paper", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "file circle check", "document", "file", "not affected", "ok", "okay", "paper" })] [FontAwesomeCategoriesAttribute(new[] { "Files", "Humanitarian" })] FileCircleCheck = 0xE5A0, /// <summary> /// The Font Awesome "file-circle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "file circle exclamation", "document", "failed", "file", "paper" })] + [FontAwesomeSearchTerms(new[] { "file circle exclamation", "document", "file", "paper" })] [FontAwesomeCategoriesAttribute(new[] { "Files", "Humanitarian" })] FileCircleExclamation = 0xE4EB, @@ -3166,21 +3138,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "file-circle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "file circle xmark", "document", "file", "paper", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "file circle xmark", "document", "file", "paper" })] [FontAwesomeCategoriesAttribute(new[] { "Files", "Humanitarian" })] FileCircleXmark = 0xE5A1, /// <summary> /// The Font Awesome "file-code" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "file code", "css", "development", "document", "html", "mysql", "sql" })] + [FontAwesomeSearchTerms(new[] { "file code", "css", "development", "document", "html" })] [FontAwesomeCategoriesAttribute(new[] { "Coding", "Files" })] FileCode = 0xF1C9, /// <summary> /// The Font Awesome "file-contract" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "file contract", "agreement", "binding", "document", "legal", "signature", "username" })] + [FontAwesomeSearchTerms(new[] { "file contract", "agreement", "binding", "document", "legal", "signature" })] [FontAwesomeCategoriesAttribute(new[] { "Security" })] FileContract = 0xF56C, @@ -3194,7 +3166,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "file-arrow-down" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "file arrow down", "archive", "document", "export", "insert", "save" })] + [FontAwesomeSearchTerms(new[] { "file arrow down", "document", "export", "save" })] [FontAwesomeCategoriesAttribute(new[] { "Files" })] FileDownload = 0xF56D, @@ -3212,31 +3184,17 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Files" })] FileExport = 0xF56E, - /// <summary> - /// The Font Awesome "file-fragment" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "file fragment", "block", "data", "partial", "piece" })] - [FontAwesomeCategoriesAttribute(new[] { "Files" })] - FileFragment = 0xE697, - - /// <summary> - /// The Font Awesome "file-half-dashed" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "file half dashed", "data", "fragment", "partial", "piece" })] - [FontAwesomeCategoriesAttribute(new[] { "Files" })] - FileHalfDashed = 0xE698, - /// <summary> /// The Font Awesome "file-image" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "file image", "document with picture", "document", "image", "img", "jpg", "photo", "png" })] + [FontAwesomeSearchTerms(new[] { "file image", "document with picture", "document", "image", "jpg", "photo", "png" })] [FontAwesomeCategoriesAttribute(new[] { "Files", "Photos + Images" })] FileImage = 0xF1C5, /// <summary> /// The Font Awesome "file-import" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "file import", "copy", "document", "insert", "send", "upload" })] + [FontAwesomeSearchTerms(new[] { "file import", "copy", "document", "send", "upload" })] [FontAwesomeCategoriesAttribute(new[] { "Files" })] FileImport = 0xF56F, @@ -3250,7 +3208,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "file-invoice-dollar" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "file invoice dollar", "$", "account", "bill", "charge", "document", "dollar-sign", "money", "payment", "receipt", "revenue", "salary", "usd" })] + [FontAwesomeSearchTerms(new[] { "file invoice dollar", "$", "account", "bill", "charge", "document", "dollar-sign", "money", "payment", "receipt", "usd" })] [FontAwesomeCategoriesAttribute(new[] { "Money" })] FileInvoiceDollar = 0xF571, @@ -3278,7 +3236,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "file-pen" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "file pen", "edit", "memo", "modify", "pen", "pencil", "update", "write" })] + [FontAwesomeSearchTerms(new[] { "file pen", "edit", "memo", "pen", "pencil", "update", "write" })] [FontAwesomeCategoriesAttribute(new[] { "Files", "Humanitarian" })] FilePen = 0xF31C, @@ -3306,14 +3264,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "file-signature" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "file signature", "john hancock", "contract", "document", "name", "username" })] + [FontAwesomeSearchTerms(new[] { "file signature", "john hancock", "contract", "document", "name" })] [FontAwesomeCategoriesAttribute(new[] { "Security" })] FileSignature = 0xF573, /// <summary> /// The Font Awesome "file-arrow-up" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "file arrow up", "document", "import", "page", "save", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "file arrow up", "document", "import", "page", "save" })] [FontAwesomeCategoriesAttribute(new[] { "Files" })] FileUpload = 0xF574, @@ -3362,14 +3320,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "filter-circle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "filter circle xmark", "cancel", "funnel", "options", "remove", "separate", "sort", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "filter circle xmark", "cancel", "funnel", "options", "remove", "separate", "sort" })] [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] FilterCircleXmark = 0xE17B, /// <summary> /// The Font Awesome "fingerprint" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "fingerprint", "human", "id", "identification", "lock", "privacy", "smudge", "touch", "unique", "unlock" })] + [FontAwesomeSearchTerms(new[] { "fingerprint", "human", "id", "identification", "lock", "smudge", "touch", "unique", "unlock" })] [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Security" })] Fingerprint = 0xF577, @@ -3453,14 +3411,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "flask" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "flask", "beaker", "chemicals", "experiment", "experimental", "knowledge", "labs", "liquid", "potion", "science", "vial" })] + [FontAwesomeSearchTerms(new[] { "flask", "beaker", "chemicals", "experiment", "experimental", "labs", "liquid", "potion", "science", "vial" })] [FontAwesomeCategoriesAttribute(new[] { "Food + Beverage", "Maps", "Medical + Health", "Science" })] Flask = 0xF0C3, /// <summary> /// The Font Awesome "flask-vial" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "flask vial", "ampule", "beaker", "chemicals", "chemistry", "experiment", "experimental", "lab", "laboratory", "labs", "liquid", "potion", "science", "test", "test tube", "vial" })] + [FontAwesomeSearchTerms(new[] { "flask vial", " beaker", " chemicals", " experiment", " experimental", " labs", " liquid", " science", " vial", "ampule", "chemistry", "lab", "laboratory", "potion", "test", "test tube" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Medical + Health", "Science" })] FlaskVial = 0xE4F3, @@ -3581,7 +3539,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "face-frown" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "face frown", "disapprove", "emoticon", "face", "frown", "frowning face", "rating", "sad", "uer" })] + [FontAwesomeSearchTerms(new[] { "face frown", "disapprove", "emoticon", "face", "frown", "frowning face", "rating", "sad" })] [FontAwesomeCategoriesAttribute(new[] { "Communication", "Emoji", "Users + People" })] Frown = 0xF119, @@ -3595,7 +3553,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "filter-circle-dollar" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "filter circle dollar", "filter", "money", "options", "premium", "separate", "sort" })] + [FontAwesomeSearchTerms(new[] { "filter circle dollar", "filter", "money", "options", "separate", "sort" })] [FontAwesomeCategoriesAttribute(new[] { "Marketing" })] FunnelDollar = 0xF662, @@ -3609,7 +3567,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "gamepad" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "gamepad", "arcade", "controller", "d-pad", "joystick", "playstore", "video", "video game" })] + [FontAwesomeSearchTerms(new[] { "gamepad", "arcade", "controller", "d-pad", "joystick", "video", "video game" })] [FontAwesomeCategoriesAttribute(new[] { "Childhood", "Devices + Hardware", "Gaming", "Maps" })] Gamepad = 0xF11B, @@ -3637,7 +3595,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "gauge-simple-high" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "gauge simple high", "dashboard", "fast", "odometer", "quick", "speed", "speedometer" })] + [FontAwesomeSearchTerms(new[] { "gauge simple high", "dashboard", "fast", "odometer", "speed", "speedometer" })] [FontAwesomeCategoriesAttribute(new[] { "Automotive" })] GaugeSimpleHigh = 0xF62A, @@ -3735,7 +3693,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "globe" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "all", "coordinates", "country", "earth", "global", "globe", "globe with meridians", "gps", "internet", "language", "localize", "location", "map", "meridians", "network", "online", "place", "planet", "translate", "travel", "world", "www" })] + [FontAwesomeSearchTerms(new[] { "all", "coordinates", "country", "earth", "global", "globe", "globe with meridians", "gps", "internet", "language", "localize", "location", "map", "meridians", "network", "online", "place", "planet", "translate", "travel", "world" })] [FontAwesomeCategoriesAttribute(new[] { "Astronomy", "Business", "Charity", "Connectivity", "Maps" })] Globe = 0xF0AC, @@ -3862,7 +3820,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "face-grin-stars" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "face grin stars", "emoticon", "eyes", "face", "grinning", "quality", "star", "star-struck", "starry-eyed", "vip" })] + [FontAwesomeSearchTerms(new[] { "face grin stars", "emoticon", "eyes", "face", "grinning", "star", "star-struck", "starry-eyed" })] [FontAwesomeCategoriesAttribute(new[] { "Emoji" })] GrinStars = 0xF587, @@ -3904,7 +3862,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "grip" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "grip", "affordance", "app", "collection", "dashboard", "drag", "drop", "grab", "grid", "handle", "launcher", "square" })] + [FontAwesomeSearchTerms(new[] { "grip", "affordance", "drag", "drop", "grab", "handle" })] [FontAwesomeCategoriesAttribute(new[] { "Editing" })] GripHorizontal = 0xF58D, @@ -3967,7 +3925,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "hammer" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "admin", "configuration", "equipment", "fix", "hammer", "maintenance", "modify", "recovery", "repair", "settings", "tool" })] + [FontAwesomeSearchTerms(new[] { "admin", "fix", "hammer", "recovery", "repair", "settings", "tool" })] [FontAwesomeCategoriesAttribute(new[] { "Construction", "Humanitarian" })] Hammer = 0xF6E3, @@ -3995,8 +3953,8 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "hand-holding-droplet" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "hand holding droplet", "blood", "carry", "covid-19", "drought", "grow", "lift", "sanitation" })] - [FontAwesomeCategoriesAttribute(new[] { "Charity", "Hands", "Medical + Health" })] + [FontAwesomeSearchTerms(new[] { "hand holding droplet", "carry", "covid-19", "drought", "grow", "lift", "sanitation" })] + [FontAwesomeCategoriesAttribute(new[] { "Charity", "Hands" })] HandHoldingDroplet = 0xF4C1, /// <summary> @@ -4009,7 +3967,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "hand-holding-heart" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "hand holding heart", "carry", "charity", "gift", "lift", "package", "wishlist" })] + [FontAwesomeSearchTerms(new[] { "hand holding heart", "carry", "charity", "gift", "lift", "package" })] [FontAwesomeCategoriesAttribute(new[] { "Charity", "Hands" })] HandHoldingHeart = 0xF4BE, @@ -4023,7 +3981,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "hand-holding-dollar" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "hand holding dollar", "$", "carry", "coupon", "dollar sign", "donate", "donation", "giving", "investment", "lift", "money", "premium", "price", "revenue", "salary" })] + [FontAwesomeSearchTerms(new[] { "hand holding dollar", "$", "carry", "dollar sign", "donation", "giving", "lift", "money", "price" })] [FontAwesomeCategoriesAttribute(new[] { "Charity", "Hands", "Money" })] HandHoldingUsd = 0xF4C0, @@ -4044,7 +4002,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "hand" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "hand", "raised hand", "backhand", "game", "halt", "palm", "raised", "raised back of hand", "request", "roshambo", "stop" })] + [FontAwesomeSearchTerms(new[] { "hand", "raised hand", "backhand", "game", "halt", "palm", "raised", "raised back of hand", "roshambo", "stop" })] [FontAwesomeCategoriesAttribute(new[] { "Hands", "Media Playback" })] HandPaper = 0xF256, @@ -4086,7 +4044,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "hand-point-up" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "hand point up", "finger", "hand", "hand-o-up", "index", "index pointing up", "point", "request", "up", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "hand point up", "finger", "hand", "hand-o-up", "index", "index pointing up", "point", "up" })] [FontAwesomeCategoriesAttribute(new[] { "Hands" })] HandPointUp = 0xF0A6, @@ -4136,29 +4094,27 @@ public enum FontAwesomeIcon /// The Font Awesome "handshake" icon unicode character. /// </summary> [FontAwesomeSearchTerms(new[] { "handshake", "agreement", "greeting", "meeting", "partnership" })] - [FontAwesomeCategoriesAttribute(new[] { "Charity", "Hands", "Humanitarian", "Political", "Shopping" })] + [FontAwesomeCategoriesAttribute(new[] { "Charity", "Hands", "Political", "Shopping" })] Handshake = 0xF2B5, /// <summary> - /// The Font Awesome "handshake" icon unicode character. - /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0xF2B5. + /// The Font Awesome "handshake-simple" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "handshake", "agreement", "greeting", "meeting", "partnership" })] - [FontAwesomeCategoriesAttribute(new[] { "Charity", "Hands", "Humanitarian", "Political", "Shopping" })] + [FontAwesomeSearchTerms(new[] { "handshake simple", "agreement", "greeting", "hand", "handshake", "meeting", "partnership", "shake" })] + [FontAwesomeCategoriesAttribute(new[] { "Charity", "Hands", "Humanitarian" })] HandshakeSimple = 0xF4C6, /// <summary> - /// The Font Awesome "handshake-slash" icon unicode character. - /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0xE060. + /// The Font Awesome "handshake-simple-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "handshake slash", "broken", "covid-19", "disabled", "social distance" })] + [FontAwesomeSearchTerms(new[] { "handshake simple slash", "broken", "covid-19", "social distance" })] [FontAwesomeCategoriesAttribute(new[] { "Hands" })] HandshakeSimpleSlash = 0xE05F, /// <summary> /// The Font Awesome "handshake-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "handshake slash", "broken", "covid-19", "disabled", "social distance" })] + [FontAwesomeSearchTerms(new[] { "handshake slash", "broken", "covid-19", "social distance" })] [FontAwesomeCategoriesAttribute(new[] { "Hands" })] HandshakeSlash = 0xE060, @@ -4172,7 +4128,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "hands-holding-child" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "hands holding child", "care", "give", "help", "hold", "parent", "protect" })] + [FontAwesomeSearchTerms(new[] { "hands holding child", "care", "give", "help", "hold", "protect" })] [FontAwesomeCategoriesAttribute(new[] { "Charity", "Childhood", "Hands", "Humanitarian", "Security" })] HandsHoldingChild = 0xE4FA, @@ -4207,7 +4163,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "helmet-safety" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "helmet safety", "construction", "hardhat", "helmet", "maintenance", "safety" })] + [FontAwesomeSearchTerms(new[] { "helmet safety", "construction", "hardhat", "helmet", "safety" })] [FontAwesomeCategoriesAttribute(new[] { "Construction", "Logistics" })] HardHat = 0xF807, @@ -4262,11 +4218,10 @@ public enum FontAwesomeIcon Headphones = 0xF025, /// <summary> - /// The Font Awesome "headphones" icon unicode character. - /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0xF025. + /// The Font Awesome "headphones-simple" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "headphones", "audio", "earbud", "headphone", "listen", "music", "sound", "speaker" })] - [FontAwesomeCategoriesAttribute(new[] { "Devices + Hardware", "Film + Video", "Music + Audio" })] + [FontAwesomeSearchTerms(new[] { "headphones simple", "audio", "listen", "music", "sound", "speaker" })] + [FontAwesomeCategoriesAttribute(new[] { "Music + Audio" })] HeadphonesAlt = 0xF58F, /// <summary> @@ -4279,35 +4234,35 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "head-side-cough" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "head side cough", "cough", "covid-19", "germs", "lungs", "respiratory", "sick", "uer" })] + [FontAwesomeSearchTerms(new[] { "head side cough", "cough", "covid-19", "germs", "lungs", "respiratory", "sick" })] [FontAwesomeCategoriesAttribute(new[] { "Medical + Health", "Users + People" })] HeadSideCough = 0xE061, /// <summary> /// The Font Awesome "head-side-cough-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "head side cough slash", "cough", "covid-19", "disabled", "germs", "lungs", "respiratory", "sick", "uer" })] + [FontAwesomeSearchTerms(new[] { "head side cough slash", "cough", "covid-19", "germs", "lungs", "respiratory", "sick" })] [FontAwesomeCategoriesAttribute(new[] { "Medical + Health", "Users + People" })] HeadSideCoughSlash = 0xE062, /// <summary> /// The Font Awesome "head-side-mask" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "head side mask", "breath", "coronavirus", "covid-19", "filter", "flu", "infection", "pandemic", "respirator", "uer", "virus" })] + [FontAwesomeSearchTerms(new[] { "head side mask", "breath", "coronavirus", "covid-19", "filter", "flu", "infection", "pandemic", "respirator", "virus" })] [FontAwesomeCategoriesAttribute(new[] { "Medical + Health", "Users + People" })] HeadSideMask = 0xE063, /// <summary> /// The Font Awesome "head-side-virus" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "head side virus", "cold", "coronavirus", "covid-19", "flu", "infection", "pandemic", "sick", "uer" })] + [FontAwesomeSearchTerms(new[] { "head side virus", "cold", "coronavirus", "covid-19", "flu", "infection", "pandemic", "sick" })] [FontAwesomeCategoriesAttribute(new[] { "Medical + Health", "Users + People" })] HeadSideVirus = 0xE064, /// <summary> /// The Font Awesome "heart" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "ace", "card", "favorite", "game", "heart", "heart suit", "like", "love", "relationship", "valentine", "wishlist" })] + [FontAwesomeSearchTerms(new[] { "black", "black heart", "blue", "blue heart", "brown", "brown heart", "card", "evil", "favorite", "game", "green", "green heart", "heart", "heart suit", "like", "love", "orange", "orange heart", "purple", "purple heart", "red heart", "relationship", "valentine", "white", "white heart", "wicked", "yellow", "yellow heart" })] [FontAwesomeCategoriesAttribute(new[] { "Charity", "Gaming", "Holidays", "Maps", "Medical + Health", "Shapes", "Shopping", "Social", "Sports + Fitness" })] Heart = 0xF004, @@ -4335,14 +4290,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "heart-circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "heart circle check", "enable", "favorite", "heart", "love", "not affected", "ok", "okay", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "heart circle check", "favorite", "heart", "love", "not affected", "ok", "okay" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Medical + Health" })] HeartCircleCheck = 0xE4FD, /// <summary> /// The Font Awesome "heart-circle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "heart circle exclamation", "failed", "favorite", "heart", "love" })] + [FontAwesomeSearchTerms(new[] { "heart circle exclamation", "favorite", "heart", "love" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Medical + Health" })] HeartCircleExclamation = 0xE4FE, @@ -4363,7 +4318,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "heart-circle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "heart circle xmark", "favorite", "heart", "love", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "heart circle xmark", "favorite", "heart", "love" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Medical + Health" })] HeartCircleXmark = 0xE501, @@ -4388,38 +4343,17 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Disaster + Crisis", "Humanitarian" })] HelmetUn = 0xE503, - /// <summary> - /// The Font Awesome "hexagon" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "hexagon", "horizontal black hexagon", "geometry", "honeycomb", "polygon", "shape" })] - [FontAwesomeCategoriesAttribute(new[] { "Shapes" })] - Hexagon = 0xF312, - - /// <summary> - /// The Font Awesome "hexagon-nodes" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "hexagon nodes", "action", "ai", "artificial intelligence", "cluster", "graph", "language", "llm", "model", "network", "neuronal" })] - [FontAwesomeCategoriesAttribute(new[] { "Charts + Diagrams", "Coding" })] - HexagonNodes = 0xE699, - - /// <summary> - /// The Font Awesome "hexagon-nodes-bolt" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "hexagon nodes bolt", "llm", "action", "ai", "artificial intelligence", "cluster", "graph", "language", "llm", "model", "network", "neuronal" })] - [FontAwesomeCategoriesAttribute(new[] { "Charts + Diagrams", "Coding" })] - HexagonNodesBolt = 0xE69A, - /// <summary> /// The Font Awesome "highlighter" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "highlighter", "edit", "marker", "modify", "sharpie", "update", "write" })] + [FontAwesomeSearchTerms(new[] { "highlighter", "edit", "marker", "sharpie", "update", "write" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Design", "Text Formatting" })] Highlighter = 0xF591, /// <summary> /// The Font Awesome "person-hiking" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person hiking", "autumn", "fall", "follow", "hike", "mountain", "outdoors", "summer", "uer", "walk" })] + [FontAwesomeSearchTerms(new[] { "person hiking", "autumn", "fall", "hike", "mountain", "outdoors", "summer", "walk" })] [FontAwesomeCategoriesAttribute(new[] { "Camping", "Nature", "Sports + Fitness", "Users + People" })] Hiking = 0xF6EC, @@ -4447,7 +4381,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "clock-rotate-left" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "clock rotate left", "rewind", "clock", "pending", "reverse", "time", "time machine", "time travel", "waiting" })] + [FontAwesomeSearchTerms(new[] { "clock rotate left", "rewind", "clock", "reverse", "time", "time machine", "time travel" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Medical + Health" })] History = 0xF1DA, @@ -4511,7 +4445,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "hospital-user" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "hospital user", "covid-19", "doctor", "network", "patient", "primary care", "uer" })] + [FontAwesomeSearchTerms(new[] { "hospital user", "covid-19", "doctor", "network", "patient", "primary care" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Medical + Health", "Users + People" })] HospitalUser = 0xF80D, @@ -4532,7 +4466,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "hot-tub-person" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "hot tub person", "jacuzzi", "spa", "uer" })] + [FontAwesomeSearchTerms(new[] { "hot tub person", "jacuzzi", "spa" })] [FontAwesomeCategoriesAttribute(new[] { "Travel + Hotel", "Users + People" })] HotTub = 0xF593, @@ -4546,21 +4480,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "hourglass-end" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "hourglass end", "hour", "hourglass done", "minute", "pending", "sand", "stopwatch", "time", "timer", "waiting" })] + [FontAwesomeSearchTerms(new[] { "hourglass end", "hour", "hourglass done", "minute", "sand", "stopwatch", "time", "timer" })] [FontAwesomeCategoriesAttribute(new[] { "Time" })] HourglassEnd = 0xF253, /// <summary> /// The Font Awesome "hourglass-half" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "hourglass half", "hour", "minute", "pending", "sand", "stopwatch", "time", "waiting" })] + [FontAwesomeSearchTerms(new[] { "hourglass half", "hour", "minute", "sand", "stopwatch", "time" })] [FontAwesomeCategoriesAttribute(new[] { "Time" })] HourglassHalf = 0xF252, /// <summary> /// The Font Awesome "hourglass-start" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "hourglass start", "hour", "minute", "sand", "stopwatch", "time", "waiting" })] + [FontAwesomeSearchTerms(new[] { "hourglass start", "hour", "minute", "sand", "stopwatch", "time" })] [FontAwesomeCategoriesAttribute(new[] { "Time" })] HourglassStart = 0xF251, @@ -4574,7 +4508,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "house-chimney-user" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "house chimney user", "covid-19", "home", "isolation", "quarantine", "uer" })] + [FontAwesomeSearchTerms(new[] { "house chimney user", "covid-19", "home", "isolation", "quarantine" })] [FontAwesomeCategoriesAttribute(new[] { "Household", "Users + People" })] HouseChimneyUser = 0xE065, @@ -4588,21 +4522,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "house-circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "house circle check", "abode", "enable", "home", "house", "not affected", "ok", "okay", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "house circle check", "abode", "home", "house", "not affected", "ok", "okay" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian" })] HouseCircleCheck = 0xE509, /// <summary> /// The Font Awesome "house-circle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "house circle exclamation", "abode", "affected", "failed", "home", "house" })] + [FontAwesomeSearchTerms(new[] { "house circle exclamation", "abode", "affected", "home", "house" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian" })] HouseCircleExclamation = 0xE50A, /// <summary> /// The Font Awesome "house-circle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "house circle xmark", "abode", "destroy", "home", "house", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "house circle xmark", "abode", "destroy", "home", "house" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian" })] HouseCircleXmark = 0xE50B, @@ -4658,7 +4592,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "house-lock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "house lock", "closed", "home", "house", "lockdown", "padlock", "privacy", "quarantine" })] + [FontAwesomeSearchTerms(new[] { "house lock", "closed", "home", "house", "lockdown", "quarantine" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Household", "Humanitarian", "Security" })] HouseLock = 0xE510, @@ -4672,21 +4606,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "house-medical-circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "house medical circle check", "clinic", "enable", "hospital", "not affected", "ok", "okay", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "house medical circle check", "clinic", "hospital", "not affected", "ok", "okay" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian", "Medical + Health" })] HouseMedicalCircleCheck = 0xE511, /// <summary> /// The Font Awesome "house-medical-circle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "house medical circle exclamation", "affected", "clinic", "failed", "hospital" })] + [FontAwesomeSearchTerms(new[] { "house medical circle exclamation", "affected", "clinic", "hospital" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian", "Medical + Health" })] HouseMedicalCircleExclamation = 0xE512, /// <summary> /// The Font Awesome "house-medical-circle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "house medical circle xmark", "clinic", "destroy", "hospital", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "house medical circle xmark", "clinic", "destroy", "hospital" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian", "Medical + Health" })] HouseMedicalCircleXmark = 0xE513, @@ -4700,7 +4634,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "house-signal" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "house signal", "abode", "building", "connect", "family", "home", "residence", "smart home", "wifi", "www" })] + [FontAwesomeSearchTerms(new[] { "house signal", "abode", "building", "connect", "family", "home", "residence", "smart home", "wifi" })] [FontAwesomeCategoriesAttribute(new[] { "Connectivity", "Household", "Humanitarian" })] HouseSignal = 0xE012, @@ -4714,7 +4648,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "house-user" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "house user", "house", "uer" })] + [FontAwesomeSearchTerms(new[] { "house user", "house" })] [FontAwesomeCategoriesAttribute(new[] { "Household", "Users + People" })] HouseUser = 0xE1B0, @@ -4756,7 +4690,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "icons" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "icons", "bolt", "category", "emoji", "heart", "image", "music", "photo", "symbols" })] + [FontAwesomeSearchTerms(new[] { "icons", "bolt", "emoji", "heart", "image", "music", "photo", "symbols" })] [FontAwesomeCategoriesAttribute(new[] { "Communication", "Design", "Social", "Text Formatting" })] Icons = 0xF86D, @@ -4770,21 +4704,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "id-badge" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "id badge", "address", "contact", "identification", "license", "profile", "uer", "username" })] + [FontAwesomeSearchTerms(new[] { "id badge", "address", "contact", "identification", "license", "profile" })] [FontAwesomeCategoriesAttribute(new[] { "Photos + Images", "Security", "Users + People" })] IdBadge = 0xF2C1, /// <summary> /// The Font Awesome "id-card" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "id card", "contact", "demographics", "document", "identification", "issued", "profile", "registration", "uer", "username" })] + [FontAwesomeSearchTerms(new[] { "id card", "contact", "demographics", "document", "identification", "issued", "profile", "registration" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Photos + Images", "Security", "Users + People" })] IdCard = 0xF2C2, /// <summary> /// The Font Awesome "id-card-clip" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "id card clip", "contact", "demographics", "document", "identification", "issued", "profile", "uer", "username" })] + [FontAwesomeSearchTerms(new[] { "id card clip", "contact", "demographics", "document", "identification", "issued", "profile" })] [FontAwesomeCategoriesAttribute(new[] { "Medical + Health", "Security", "Users + People" })] IdCardAlt = 0xF47F, @@ -4798,14 +4732,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "image" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "image", "album", "img", "landscape", "photo", "picture" })] + [FontAwesomeSearchTerms(new[] { "image", "album", "landscape", "photo", "picture" })] [FontAwesomeCategoriesAttribute(new[] { "Maps", "Photos + Images", "Social" })] Image = 0xF03E, /// <summary> /// The Font Awesome "images" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "images", "album", "img", "landscape", "photo", "picture" })] + [FontAwesomeSearchTerms(new[] { "images", "album", "landscape", "photo", "picture" })] [FontAwesomeCategoriesAttribute(new[] { "Maps", "Photos + Images", "Social" })] Images = 0xF302, @@ -4858,12 +4792,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Maps" })] InfoCircle = 0xF05A, - /// <summary> - /// The Font Awesome "instagramsquare" icon unicode character. - /// </summary> - [Obsolete] - InstagramSquare = 0xF955, - /// <summary> /// The Font Awesome "italic" icon unicode character. /// </summary> @@ -4993,7 +4921,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "landmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "landmark", "building", "classical", "historic", "memorable", "monument", "museum", "politics", "society" })] + [FontAwesomeSearchTerms(new[] { "landmark", "building", "classical", "historic", "memorable", "monument", "museum", "politics" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Business", "Humanitarian", "Maps", "Money" })] Landmark = 0xF66F, @@ -5028,14 +4956,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "laptop" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "computer", "cpu", "dell", "demo", "device", "fabook", "fb", "laptop", "mac", "macbook", "machine", "pc", "personal" })] + [FontAwesomeSearchTerms(new[] { "computer", "cpu", "dell", "demo", "device", "laptop", "mac", "macbook", "machine", "pc", "personal" })] [FontAwesomeCategoriesAttribute(new[] { "Devices + Hardware", "Humanitarian" })] Laptop = 0xF109, /// <summary> /// The Font Awesome "laptop-code" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "laptop code", "computer", "cpu", "dell", "demo", "develop", "device", "fabook", "fb", "mac", "macbook", "machine", "mysql", "pc", "sql" })] + [FontAwesomeSearchTerms(new[] { "laptop code", "computer", "cpu", "dell", "demo", "develop", "device", "mac", "macbook", "machine", "pc" })] [FontAwesomeCategoriesAttribute(new[] { "Coding", "Education" })] LaptopCode = 0xF5FC, @@ -5091,7 +5019,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "layer-group" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "layer group", "arrange", "category", "develop", "layers", "map", "platform", "stack" })] + [FontAwesomeSearchTerms(new[] { "layer group", "arrange", "develop", "layers", "map", "stack" })] [FontAwesomeCategoriesAttribute(new[] { "Design", "Maps" })] LayerGroup = 0xF5FD, @@ -5148,7 +5076,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "lightbulb" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "lightbulb", "bulb", "bulb", "comic", "comic", "electric", "electric", "energy", "idea", "idea", "innovation", "inspiration", "inspiration", "light", "light bulb", "mechanical" })] + [FontAwesomeSearchTerms(new[] { "lightbulb", " comic", " electric", " idea", " innovation", " inspiration", " light", " light bulb", " bulb", "bulb", "comic", "electric", "energy", "idea", "inspiration", "mechanical" })] [FontAwesomeCategoriesAttribute(new[] { "Energy", "Household", "Maps", "Marketing" })] Lightbulb = 0xF0EB, @@ -5176,28 +5104,28 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "list" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "list", "bullet", "category", "cheatsheet", "checklist", "completed", "done", "finished", "ol", "summary", "todo", "ul" })] + [FontAwesomeSearchTerms(new[] { "list", "checklist", "completed", "done", "finished", "ol", "todo", "ul" })] [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] List = 0xF03A, /// <summary> /// The Font Awesome "rectangle-list" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "rectangle list", "cheatsheet", "checklist", "completed", "done", "finished", "ol", "summary", "todo", "ul" })] + [FontAwesomeSearchTerms(new[] { "rectangle list", "checklist", "completed", "done", "finished", "ol", "todo", "ul" })] [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] ListAlt = 0xF022, /// <summary> /// The Font Awesome "list-ol" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "list ol", "cheatsheet", "checklist", "completed", "done", "finished", "numbers", "ol", "summary", "todo", "ul" })] + [FontAwesomeSearchTerms(new[] { "list ol", "checklist", "completed", "done", "finished", "numbers", "ol", "todo", "ul" })] [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] ListOl = 0xF0CB, /// <summary> /// The Font Awesome "list-ul" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "list ul", "bullet", "cheatsheet", "checklist", "completed", "done", "finished", "ol", "summary", "survey", "todo", "ul" })] + [FontAwesomeSearchTerms(new[] { "list ul", "checklist", "completed", "done", "finished", "ol", "todo", "ul" })] [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] ListUl = 0xF0CA, @@ -5225,21 +5153,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "location-pin-lock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "location pin lock", "closed", "lockdown", "map", "padlock", "privacy", "quarantine" })] + [FontAwesomeSearchTerms(new[] { "location pin lock", "closed", "lockdown", "map", "quarantine" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Maps" })] LocationPinLock = 0xE51F, /// <summary> /// The Font Awesome "lock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "admin", "closed", "lock", "locked", "open", "padlock", "password", "privacy", "private", "protect", "security" })] + [FontAwesomeSearchTerms(new[] { "admin", "closed", "lock", "locked", "open", "password", "private", "protect", "security" })] [FontAwesomeCategoriesAttribute(new[] { "Security" })] Lock = 0xF023, /// <summary> /// The Font Awesome "lock-open" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "lock open", "admin", "lock", "open", "padlock", "password", "privacy", "private", "protect", "security", "unlock" })] + [FontAwesomeSearchTerms(new[] { "lock open", "admin", "lock", "open", "password", "private", "protect", "security", "unlock" })] [FontAwesomeCategoriesAttribute(new[] { "Security" })] LockOpen = 0xF3C1, @@ -5274,7 +5202,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "up-long" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "up long", "long-arrow-up", "upgrade", "upload" })] + [FontAwesomeSearchTerms(new[] { "up long", "long-arrow-up", "upload" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] LongArrowAltUp = 0xF30C, @@ -5323,28 +5251,28 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "magnifying-glass-arrow-right" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "magnifying glass arrow right", "find", "magnifier", "next", "search" })] + [FontAwesomeSearchTerms(new[] { "magnifying glass arrow right", "find", "next", "search" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Humanitarian", "Marketing" })] MagnifyingGlassArrowRight = 0xE521, /// <summary> /// The Font Awesome "magnifying-glass-chart" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "magnifying glass chart", "analysis", "chart", "data", "graph", "intelligence", "magnifier", "market", "revenue" })] + [FontAwesomeSearchTerms(new[] { "magnifying glass chart", " data", " graph", " intelligence", "analysis", "chart", "market" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Humanitarian", "Marketing" })] MagnifyingGlassChart = 0xE522, /// <summary> /// The Font Awesome "envelopes-bulk" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "envelopes bulk", "archive", "envelope", "letter", "newsletter", "offer", "post office", "postal", "postcard", "send", "stamp", "usps" })] + [FontAwesomeSearchTerms(new[] { "envelopes bulk", "archive", "envelope", "letter", "post office", "postal", "postcard", "send", "stamp", "usps" })] [FontAwesomeCategoriesAttribute(new[] { "Marketing" })] MailBulk = 0xF674, /// <summary> /// The Font Awesome "person" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person", "default", "man", "person standing", "stand", "standing", "uer", "woman" })] + [FontAwesomeSearchTerms(new[] { "person", "man", "person standing", "stand", "standing", "woman" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Maps", "Users + People" })] Male = 0xF183, @@ -5407,7 +5335,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "marker" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "marker", "design", "edit", "modify", "sharpie", "update", "write" })] + [FontAwesomeSearchTerms(new[] { "marker", "design", "edit", "sharpie", "update", "write" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Design" })] Marker = 0xF5A1, @@ -5421,7 +5349,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "mars-and-venus-burst" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "mars and venus burst", "gender", "uer", "violence" })] + [FontAwesomeSearchTerms(new[] { "mars and venus burst", "gender", "violence" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Security", "Users + People" })] MarsAndVenusBurst = 0xE523, @@ -5484,7 +5412,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "medal" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "award", "guarantee", "medal", "quality", "ribbon", "sports medal", "star", "trophy", "warranty" })] + [FontAwesomeSearchTerms(new[] { "award", "medal", "ribbon", "sports medal", "star", "trophy" })] [FontAwesomeCategoriesAttribute(new[] { "Sports + Fitness" })] Medal = 0xF5A2, @@ -5498,7 +5426,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "face-meh" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "face meh", "deadpan", "default", "emoticon", "face", "meh", "neutral", "neutral face", "rating", "uer" })] + [FontAwesomeSearchTerms(new[] { "face meh", "deadpan", "emoticon", "face", "meh", "neutral", "neutral face", "rating" })] [FontAwesomeCategoriesAttribute(new[] { "Communication", "Emoji", "Users + People" })] Meh = 0xF11A, @@ -5554,35 +5482,35 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "microphone" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "microphone", "address", "audio", "information", "podcast", "public", "record", "sing", "sound", "talking", "voice" })] + [FontAwesomeSearchTerms(new[] { "microphone", "address", "audio", "information", "podcast", "public", "record", "sing", "sound", "voice" })] [FontAwesomeCategoriesAttribute(new[] { "Communication", "Film + Video", "Music + Audio", "Toggle" })] Microphone = 0xF130, /// <summary> /// The Font Awesome "microphone-lines" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "microphone lines", "audio", "mic", "microphone", "music", "podcast", "record", "sing", "sound", "studio", "studio microphone", "talking", "voice" })] + [FontAwesomeSearchTerms(new[] { "microphone lines", "audio", "mic", "microphone", "music", "podcast", "record", "sing", "sound", "studio", "studio microphone", "voice" })] [FontAwesomeCategoriesAttribute(new[] { "Communication", "Film + Video", "Music + Audio" })] MicrophoneAlt = 0xF3C9, /// <summary> /// The Font Awesome "microphone-lines-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "microphone lines slash", "audio", "disable", "disabled", "disconnect", "disconnect", "mute", "podcast", "record", "sing", "sound", "voice" })] + [FontAwesomeSearchTerms(new[] { "microphone lines slash", "audio", "disable", "mute", "podcast", "record", "sing", "sound", "voice" })] [FontAwesomeCategoriesAttribute(new[] { "Communication", "Film + Video", "Music + Audio" })] MicrophoneAltSlash = 0xF539, /// <summary> /// The Font Awesome "microphone-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "microphone slash", "audio", "disable", "disabled", "mute", "podcast", "record", "sing", "sound", "voice" })] + [FontAwesomeSearchTerms(new[] { "microphone slash", "audio", "disable", "mute", "podcast", "record", "sing", "sound", "voice" })] [FontAwesomeCategoriesAttribute(new[] { "Communication", "Film + Video", "Music + Audio", "Toggle" })] MicrophoneSlash = 0xF131, /// <summary> /// The Font Awesome "microscope" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "covid-19", "electron", "knowledge", "lens", "microscope", "optics", "science", "shrink", "testing", "tool" })] + [FontAwesomeSearchTerms(new[] { "covid-19", "electron", "lens", "microscope", "optics", "science", "shrink", "testing", "tool" })] [FontAwesomeCategoriesAttribute(new[] { "Education", "Humanitarian", "Medical + Health", "Science" })] Microscope = 0xF610, @@ -5656,80 +5584,73 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Communication", "Devices + Hardware", "Humanitarian" })] MobileScreen = 0xF3CF, - /// <summary> - /// The Font Awesome "mobile-vibrate" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "mobile vibrate", "android", "call", "cell", "cell phone", "device", "haptic", "mobile", "mobile phone", "notification", "number", "phone", "screen", "telephone", "text" })] - [FontAwesomeCategoriesAttribute(new[] { "Communication", "Devices + Hardware" })] - MobileVibrate = 0xE816, - /// <summary> /// The Font Awesome "money-bill" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "money bill", "buy", "cash", "checkout", "coupon", "investment", "money", "payment", "premium", "price", "purchase", "revenue", "salary" })] + [FontAwesomeSearchTerms(new[] { "money bill", "buy", "cash", "checkout", "money", "payment", "price", "purchase" })] [FontAwesomeCategoriesAttribute(new[] { "Maps", "Money" })] MoneyBill = 0xF0D6, /// <summary> /// The Font Awesome "money-bill-1" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "money bill 1", "buy", "cash", "checkout", "money", "payment", "premium", "price", "purchase", "salary" })] + [FontAwesomeSearchTerms(new[] { "money bill 1", "buy", "cash", "checkout", "money", "payment", "price", "purchase" })] [FontAwesomeCategoriesAttribute(new[] { "Maps", "Money" })] MoneyBillAlt = 0xF3D1, /// <summary> /// The Font Awesome "money-bills" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "money bills", "atm", "cash", "investment", "money", "moolah", "premium", "revenue", "salary" })] + [FontAwesomeSearchTerms(new[] { "money bills", "atm", "cash", "money", "moolah" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Money" })] MoneyBills = 0xE1F3, /// <summary> /// The Font Awesome "money-bill-transfer" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "money bill transfer", "bank", "conversion", "deposit", "investment", "money", "salary", "transfer", "withdrawal" })] + [FontAwesomeSearchTerms(new[] { "money bill transfer", "bank", "conversion", "deposit", "money", "transfer", "withdrawal" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Money" })] MoneyBillTransfer = 0xE528, /// <summary> /// The Font Awesome "money-bill-trend-up" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "money bill trend up", "bank", "bonds", "inflation", "investment", "market", "revenue", "salary", "stocks", "trade" })] + [FontAwesomeSearchTerms(new[] { "money bill trend up", "bank", "bonds", "inflation", "market", "stocks", "trade" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Money" })] MoneyBillTrendUp = 0xE529, /// <summary> /// The Font Awesome "money-bill-wave" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "money bill wave", "buy", "cash", "checkout", "money", "payment", "premium", "price", "purchase", "salary" })] + [FontAwesomeSearchTerms(new[] { "money bill wave", "buy", "cash", "checkout", "money", "payment", "price", "purchase" })] [FontAwesomeCategoriesAttribute(new[] { "Money" })] MoneyBillWave = 0xF53A, /// <summary> /// The Font Awesome "money-bill-1-wave" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "money bill 1 wave", "buy", "cash", "checkout", "money", "payment", "premium", "price", "purchase", "salary" })] + [FontAwesomeSearchTerms(new[] { "money bill 1 wave", "buy", "cash", "checkout", "money", "payment", "price", "purchase" })] [FontAwesomeCategoriesAttribute(new[] { "Money" })] MoneyBillWaveAlt = 0xF53B, /// <summary> /// The Font Awesome "money-bill-wheat" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "money bill wheat", "agribusiness", "agriculture", "farming", "food", "investment", "livelihood", "subsidy" })] + [FontAwesomeSearchTerms(new[] { "money bill wheat", "agribusiness", "agriculture", "farming", "food", "livelihood", "subsidy" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Money" })] MoneyBillWheat = 0xE52A, /// <summary> /// The Font Awesome "money-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "money check", "bank check", "buy", "checkout", "cheque", "money", "payment", "price", "purchase", "salary" })] + [FontAwesomeSearchTerms(new[] { "money check", "bank check", "buy", "checkout", "cheque", "money", "payment", "price", "purchase" })] [FontAwesomeCategoriesAttribute(new[] { "Money", "Shopping" })] MoneyCheck = 0xF53C, /// <summary> /// The Font Awesome "money-check-dollar" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "money check dollar", "bank check", "buy", "checkout", "cheque", "money", "payment", "price", "purchase", "salary" })] + [FontAwesomeSearchTerms(new[] { "money check dollar", "bank check", "buy", "checkout", "cheque", "money", "payment", "price", "purchase" })] [FontAwesomeCategoriesAttribute(new[] { "Money", "Shopping" })] MoneyCheckAlt = 0xF53D, @@ -5862,21 +5783,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "newspaper" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "article", "editorial", "headline", "journal", "journalism", "news", "newsletter", "newspaper", "paper", "press" })] + [FontAwesomeSearchTerms(new[] { "article", "editorial", "headline", "journal", "journalism", "news", "newspaper", "paper", "press" })] [FontAwesomeCategoriesAttribute(new[] { "Maps", "Writing" })] Newspaper = 0xF1EA, - /// <summary> - /// The Font Awesome "non-binary" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "non binary", "female", "gender", "male", "nb", "queer" })] - [FontAwesomeCategoriesAttribute(new[] { "Genders" })] - NonBinary = 0xE807, - /// <summary> /// The Font Awesome "notdef" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "notdef", "404", "close", "missing", "not found" })] + [FontAwesomeSearchTerms(new[] { "notdef", "close", "missing" })] [FontAwesomeCategoriesAttribute(new[] { "Coding", "Writing" })] Notdef = 0xE1FE, @@ -5908,13 +5822,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Design" })] ObjectUngroup = 0xF248, - /// <summary> - /// The Font Awesome "octagon" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "octagon", "octagonal", "shape", "sign", "stop", "stop sign" })] - [FontAwesomeCategoriesAttribute(new[] { "Shapes" })] - Octagon = 0xF306, - /// <summary> /// The Font Awesome "oil-can" icon unicode character. /// </summary> @@ -5960,14 +5867,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "paintbrush" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "acrylic", "art", "brush", "color", "fill", "modify", "paint", "paintbrush", "painting", "pigment", "watercolor" })] + [FontAwesomeSearchTerms(new[] { "acrylic", "art", "brush", "color", "fill", "paint", "paintbrush", "painting", "pigment", "watercolor" })] [FontAwesomeCategoriesAttribute(new[] { "Design", "Editing" })] PaintBrush = 0xF1FC, /// <summary> /// The Font Awesome "paint-roller" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "paint roller", "acrylic", "art", "brush", "color", "fill", "maintenance", "paint", "pigment", "watercolor" })] + [FontAwesomeSearchTerms(new[] { "paint roller", "acrylic", "art", "brush", "color", "fill", "paint", "pigment", "watercolor" })] [FontAwesomeCategoriesAttribute(new[] { "Construction", "Design" })] PaintRoller = 0xF5AA, @@ -5988,7 +5895,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "panorama" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "panorama", "image", "img", "landscape", "photo", "wide" })] + [FontAwesomeSearchTerms(new[] { "panorama", "image", "landscape", "photo", "wide" })] [FontAwesomeCategoriesAttribute(new[] { "Photos + Images" })] Panorama = 0xE209, @@ -6079,105 +5986,98 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "pen" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "ballpoint", "design", "edit", "modify", "pen", "update", "write" })] + [FontAwesomeSearchTerms(new[] { "ballpoint", "design", "edit", "pen", "update", "write" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Design", "Editing", "Writing" })] Pen = 0xF304, /// <summary> /// The Font Awesome "pen-clip" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "pen clip", "design", "edit", "modify", "update", "write" })] + [FontAwesomeSearchTerms(new[] { "pen clip", "design", "edit", "update", "write" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Design", "Editing", "Writing" })] PenAlt = 0xF305, /// <summary> /// The Font Awesome "pencil" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "lower left pencil", "design", "draw", "edit", "lead", "maintenance", "modify", "pencil", "update", "write" })] + [FontAwesomeSearchTerms(new[] { "lower left pencil", "design", "draw", "edit", "lead", "pencil", "update", "write" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Construction", "Design", "Editing", "Writing" })] PencilAlt = 0xF303, /// <summary> /// The Font Awesome "pen-ruler" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "pen ruler", "design", "draft", "draw", "maintenance", "modify", "pencil" })] + [FontAwesomeSearchTerms(new[] { "pen ruler", "design", "draft", "draw", "pencil" })] [FontAwesomeCategoriesAttribute(new[] { "Construction", "Design", "Editing" })] PencilRuler = 0xF5AE, /// <summary> /// The Font Awesome "pen-fancy" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "pen fancy", "black nib", "design", "edit", "fountain", "fountain pen", "modify", "nib", "pen", "update", "write" })] + [FontAwesomeSearchTerms(new[] { "pen fancy", "black nib", "design", "edit", "fountain", "fountain pen", "nib", "pen", "update", "write" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Design", "Editing" })] PenFancy = 0xF5AC, /// <summary> /// The Font Awesome "pen-nib" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "pen nib", "design", "edit", "fountain pen", "modify", "update", "write" })] + [FontAwesomeSearchTerms(new[] { "pen nib", "design", "edit", "fountain pen", "update", "write" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Design", "Editing" })] PenNib = 0xF5AD, /// <summary> /// The Font Awesome "square-pen" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "square pen", "edit", "modify", "pencil-square", "update", "write" })] + [FontAwesomeSearchTerms(new[] { "square pen", "edit", "pencil-square", "update", "write" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Editing", "Writing" })] PenSquare = 0xF14B, - /// <summary> - /// The Font Awesome "pentagon" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "5", "five", "pentagon", "shape" })] - [FontAwesomeCategoriesAttribute(new[] { "Shapes" })] - Pentagon = 0xE790, - /// <summary> /// The Font Awesome "people-arrows" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "people arrows", "conversation", "discussion", "distance", "insert", "isolation", "separate", "social distancing", "talk", "talking", "together", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "people arrows", "distance", "isolation", "separate", "social distancing", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] PeopleArrows = 0xE068, /// <summary> /// The Font Awesome "people-carry-box" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "people carry box", "together", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "people carry box", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Moving", "Users + People" })] PeopleCarry = 0xF4CE, /// <summary> /// The Font Awesome "people-group" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "people group", "crowd", "family", "group", "team", "together", "uer" })] + [FontAwesomeSearchTerms(new[] { "people group", "family", "group", "team" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Marketing", "Users + People" })] PeopleGroup = 0xE533, /// <summary> /// The Font Awesome "people-line" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "people line", "crowd", "group", "need", "together", "uer" })] + [FontAwesomeSearchTerms(new[] { "people line", "group", "need" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] PeopleLine = 0xE534, /// <summary> /// The Font Awesome "people-pulling" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "people pulling", "forced return", "together", "uer", "yanking" })] + [FontAwesomeSearchTerms(new[] { "people pulling", "forced return", "yanking" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Security", "Users + People" })] PeoplePulling = 0xE535, /// <summary> /// The Font Awesome "people-robbery" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "people robbery", "criminal", "hands up", "looting", "robbery", "steal", "uer" })] + [FontAwesomeSearchTerms(new[] { "people robbery", "criminal", "hands up", "looting", "robbery", "steal" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Security", "Users + People" })] PeopleRobbery = 0xE536, /// <summary> /// The Font Awesome "people-roof" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "people roof", "crowd", "family", "group", "manage", "people", "safe", "shelter", "together", "uer" })] + [FontAwesomeSearchTerms(new[] { "people roof", "family", "group", "manage", "people", "safe", "shelter" })] [FontAwesomeCategoriesAttribute(new[] { "Camping", "Household", "Humanitarian", "Users + People" })] PeopleRoof = 0xE537, @@ -6207,105 +6107,105 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "person-arrow-down-to-line" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person arrow down to line", "ground", "indigenous", "insert", "native", "uer" })] + [FontAwesomeSearchTerms(new[] { "person arrow down to line", "ground", "indigenous", "native" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] PersonArrowDownToLine = 0xE538, /// <summary> /// The Font Awesome "person-arrow-up-from-line" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person arrow up from line", "population", "rise", "uer", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "person arrow up from line", "population", "rise" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] PersonArrowUpFromLine = 0xE539, /// <summary> /// The Font Awesome "person-booth" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person booth", "changing room", "curtain", "uer", "vote", "voting" })] + [FontAwesomeSearchTerms(new[] { "person booth", "changing room", "curtain", "vote", "voting" })] [FontAwesomeCategoriesAttribute(new[] { "Political", "Shopping", "Users + People" })] PersonBooth = 0xF756, /// <summary> /// The Font Awesome "person-breastfeeding" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person breastfeeding", "baby", "child", "infant", "mother", "nutrition", "parent", "sustenance", "uer" })] + [FontAwesomeSearchTerms(new[] { "person breastfeeding", "baby", "child", "infant", "mother", "nutrition", "sustenance" })] [FontAwesomeCategoriesAttribute(new[] { "Childhood", "Humanitarian", "Medical + Health", "Users + People" })] PersonBreastfeeding = 0xE53A, /// <summary> /// The Font Awesome "person-burst" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person burst", "abuse", "accident", "crash", "explode", "uer", "violence" })] + [FontAwesomeSearchTerms(new[] { "person burst", "abuse", "accident", "crash", "explode", "violence" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Security", "Users + People" })] PersonBurst = 0xE53B, /// <summary> /// The Font Awesome "person-cane" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person cane", "aging", "cane", "elderly", "old", "staff", "uer" })] + [FontAwesomeSearchTerms(new[] { "person cane", "aging", "cane", "elderly", "old", "staff" })] [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Humanitarian", "Medical + Health", "Users + People" })] PersonCane = 0xE53C, /// <summary> /// The Font Awesome "person-chalkboard" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person chalkboard", "blackboard", "instructor", "keynote", "lesson", "presentation", "teacher", "uer" })] + [FontAwesomeSearchTerms(new[] { "person chalkboard", "blackboard", "instructor", "keynote", "lesson", "presentation", "teacher" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Education", "Humanitarian", "Users + People" })] PersonChalkboard = 0xE53D, /// <summary> /// The Font Awesome "person-circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person circle check", "approved", "enable", "not affected", "ok", "okay", "uer", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "person circle check", "approved", "not affected", "ok", "okay" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] PersonCircleCheck = 0xE53E, /// <summary> /// The Font Awesome "person-circle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person circle exclamation", "affected", "alert", "failed", "lost", "missing", "uer" })] + [FontAwesomeSearchTerms(new[] { "person circle exclamation", "affected", "alert", "lost", "missing" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] PersonCircleExclamation = 0xE53F, /// <summary> /// The Font Awesome "person-circle-minus" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person circle minus", "delete", "remove", "uer" })] + [FontAwesomeSearchTerms(new[] { "person circle minus", "delete", "remove" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] PersonCircleMinus = 0xE540, /// <summary> /// The Font Awesome "person-circle-plus" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person circle plus", "add", "follow", "found", "uer" })] + [FontAwesomeSearchTerms(new[] { "person circle plus", "add", "found" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] PersonCirclePlus = 0xE541, /// <summary> /// The Font Awesome "person-circle-question" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person circle question", "faq", "lost", "missing", "request", "uer" })] + [FontAwesomeSearchTerms(new[] { "person circle question", "lost", "missing" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] PersonCircleQuestion = 0xE542, /// <summary> /// The Font Awesome "person-circle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person circle xmark", "dead", "removed", "uer", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "person circle xmark", "dead", "removed" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] PersonCircleXmark = 0xE543, /// <summary> /// The Font Awesome "person-digging" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person digging", "bury", "construction", "debris", "dig", "maintenance", "men at work", "uer" })] + [FontAwesomeSearchTerms(new[] { "person digging", "bury", "construction", "debris", "dig", "men at work" })] [FontAwesomeCategoriesAttribute(new[] { "Construction", "Humanitarian", "Users + People" })] PersonDigging = 0xF85E, /// <summary> /// The Font Awesome "person-dress-burst" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person dress burst", "abuse", "accident", "crash", "explode", "uer", "violence" })] + [FontAwesomeSearchTerms(new[] { "person dress burst", "abuse", "accident", "crash", "explode", "violence" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Security", "Users + People" })] PersonDressBurst = 0xE544, @@ -6319,112 +6219,112 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "person-falling" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person falling", "accident", "fall", "trip", "uer" })] + [FontAwesomeSearchTerms(new[] { "person falling", "accident", "fall", "trip" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] PersonFalling = 0xE546, /// <summary> /// The Font Awesome "person-falling-burst" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person falling burst", "accident", "crash", "death", "fall", "homicide", "murder", "uer" })] + [FontAwesomeSearchTerms(new[] { "person falling burst", "accident", "crash", "death", "fall", "homicide", "murder" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Security", "Users + People" })] PersonFallingBurst = 0xE547, /// <summary> /// The Font Awesome "person-half-dress" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person half dress", "gender", "man", "restroom", "transgender", "uer", "woman" })] + [FontAwesomeSearchTerms(new[] { "person half dress", "gender", "man", "restroom", "transgender", "woman" })] [FontAwesomeCategoriesAttribute(new[] { "Genders", "Humanitarian", "Medical + Health", "Users + People" })] PersonHalfDress = 0xE548, /// <summary> /// The Font Awesome "person-harassing" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person harassing", "abuse", "scream", "shame", "shout", "uer", "yell" })] + [FontAwesomeSearchTerms(new[] { "person harassing", "abuse", "scream", "shame", "shout", "yell" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Security", "Users + People" })] PersonHarassing = 0xE549, /// <summary> /// The Font Awesome "person-military-pointing" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person military pointing", "army", "customs", "guard", "uer" })] + [FontAwesomeSearchTerms(new[] { "person military pointing", "army", "customs", "guard" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Security", "Users + People" })] PersonMilitaryPointing = 0xE54A, /// <summary> /// The Font Awesome "person-military-rifle" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person military rifle", "armed forces", "army", "military", "rifle", "uer", "war" })] + [FontAwesomeSearchTerms(new[] { "person military rifle", "armed forces", "army", "military", "rifle", "war" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Security", "Users + People" })] PersonMilitaryRifle = 0xE54B, /// <summary> /// The Font Awesome "person-military-to-person" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person military to person", "civilian", "coordination", "military", "uer" })] + [FontAwesomeSearchTerms(new[] { "person military to person", "civilian", "coordination", "military" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Security", "Users + People" })] PersonMilitaryToPerson = 0xE54C, /// <summary> /// The Font Awesome "person-pregnant" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person pregnant", "baby", "birth", "child", "parent", "pregnant", "pregnant woman", "uer", "woman" })] + [FontAwesomeSearchTerms(new[] { "person pregnant", "baby", "birth", "child", "pregnant", "pregnant woman", "woman" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] PersonPregnant = 0xE31E, /// <summary> /// The Font Awesome "person-rays" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person rays", "affected", "focus", "shine", "uer" })] + [FontAwesomeSearchTerms(new[] { "person rays", "affected", "focus", "shine" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Marketing", "Users + People" })] PersonRays = 0xE54D, /// <summary> /// The Font Awesome "person-rifle" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person rifle", "army", "combatant", "gun", "military", "rifle", "uer", "war" })] + [FontAwesomeSearchTerms(new[] { "person rifle", "army", "combatant", "gun", "military", "rifle", "war" })] [FontAwesomeCategoriesAttribute(new[] { "Disaster + Crisis", "Humanitarian", "Security", "Users + People" })] PersonRifle = 0xE54E, /// <summary> /// The Font Awesome "person-shelter" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person shelter", "house", "inside", "roof", "safe", "safety", "shelter", "uer" })] + [FontAwesomeSearchTerms(new[] { "person shelter", "house", "inside", "roof", "safe", "safety", "shelter" })] [FontAwesomeCategoriesAttribute(new[] { "Camping", "Humanitarian", "Security", "Users + People" })] PersonShelter = 0xE54F, /// <summary> /// The Font Awesome "person-through-window" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person through window", "door", "exit", "forced entry", "leave", "robbery", "steal", "uer", "window" })] + [FontAwesomeSearchTerms(new[] { "person through window", "door", "exit", "forced entry", "leave", "robbery", "steal", "window" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Security", "Users + People" })] PersonThroughWindow = 0xE5A9, /// <summary> /// The Font Awesome "person-walking-arrow-loop-left" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person walking arrow loop left", "follow", "population return", "return", "uer" })] + [FontAwesomeSearchTerms(new[] { "person walking arrow loop left", "population return", "return" })] [FontAwesomeCategoriesAttribute(new[] { "Disaster + Crisis", "Humanitarian", "Users + People" })] PersonWalkingArrowLoopLeft = 0xE551, /// <summary> /// The Font Awesome "person-walking-arrow-right" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person walking arrow right", "exit", "follow", "internally displaced", "leave", "refugee", "uer" })] + [FontAwesomeSearchTerms(new[] { "person walking arrow right", "exit", "internally displaced", "leave", "refugee" })] [FontAwesomeCategoriesAttribute(new[] { "Disaster + Crisis", "Humanitarian", "Users + People" })] PersonWalkingArrowRight = 0xE552, /// <summary> /// The Font Awesome "person-walking-dashed-line-arrow-right" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person walking dashed line arrow right", "exit", "follow", "refugee", "uer" })] + [FontAwesomeSearchTerms(new[] { "person walking dashed line arrow right", "exit", "refugee" })] [FontAwesomeCategoriesAttribute(new[] { "Disaster + Crisis", "Humanitarian", "Users + People" })] PersonWalkingDashedLineArrowRight = 0xE553, /// <summary> /// The Font Awesome "person-walking-luggage" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person walking luggage", "bag", "baggage", "briefcase", "carry-on", "deployment", "follow", "rolling", "uer" })] + [FontAwesomeSearchTerms(new[] { "person walking luggage", "bag", "baggage", "briefcase", "carry-on", "deployment", "rolling" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Travel + Hotel", "Users + People" })] PersonWalkingLuggage = 0xE554, @@ -6445,7 +6345,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "phone" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "left hand telephone receiver", "call", "earphone", "number", "phone", "receiver", "support", "talking", "telephone", "telephone receiver", "voice" })] + [FontAwesomeSearchTerms(new[] { "left hand telephone receiver", "call", "earphone", "number", "phone", "receiver", "support", "telephone", "telephone receiver", "voice" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Communication", "Maps" })] Phone = 0xF095, @@ -6459,7 +6359,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "phone-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "phone slash", "call", "cancel", "disabled", "disconnect", "earphone", "mute", "number", "support", "telephone", "voice" })] + [FontAwesomeSearchTerms(new[] { "phone slash", "call", "cancel", "earphone", "mute", "number", "support", "telephone", "voice" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Communication" })] PhoneSlash = 0xF3DD, @@ -6480,7 +6380,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "phone-volume" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "phone volume", "call", "earphone", "number", "ring", "ringing", "sound", "support", "talking", "telephone", "voice", "volume-control-phone" })] + [FontAwesomeSearchTerms(new[] { "phone volume", "call", "earphone", "number", "sound", "support", "telephone", "voice", "volume-control-phone" })] [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Business", "Communication", "Maps", "Media Playback" })] PhoneVolume = 0xF2A0, @@ -6494,7 +6394,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "piggy-bank" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "piggy bank", "bank", "salary", "save", "savings" })] + [FontAwesomeSearchTerms(new[] { "piggy bank", "bank", "save", "savings" })] [FontAwesomeCategoriesAttribute(new[] { "Charity", "Money", "Political" })] PiggyBank = 0xF4D3, @@ -6530,27 +6430,27 @@ public enum FontAwesomeIcon /// The Font Awesome "plane-arrival" icon unicode character. /// </summary> [FontAwesomeSearchTerms(new[] { "plane arrival", "aeroplane", "airplane", "airplane arrival", "airport", "arrivals", "arriving", "destination", "fly", "land", "landing", "location", "mode", "travel", "trip" })] - [FontAwesomeCategoriesAttribute(new[] { "Transportation", "Travel + Hotel" })] + [FontAwesomeCategoriesAttribute(new[] { "Travel + Hotel" })] PlaneArrival = 0xF5AF, /// <summary> /// The Font Awesome "plane-circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "plane circle check", "airplane", "airport", "enable", "flight", "fly", "not affected", "ok", "okay", "travel", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "plane circle check", "airplane", "airport", "flight", "fly", "not affected", "ok", "okay", "travel" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics", "Travel + Hotel" })] PlaneCircleCheck = 0xE555, /// <summary> /// The Font Awesome "plane-circle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "plane circle exclamation", "affected", "airplane", "airport", "failed", "flight", "fly", "travel" })] + [FontAwesomeSearchTerms(new[] { "plane circle exclamation", "affected", "airplane", "airport", "flight", "fly", "travel" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics", "Travel + Hotel" })] PlaneCircleExclamation = 0xE556, /// <summary> /// The Font Awesome "plane-circle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "plane circle xmark", "airplane", "airport", "destroy", "flight", "fly", "travel", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "plane circle xmark", "airplane", "airport", "destroy", "flight", "fly", "travel" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics", "Travel + Hotel" })] PlaneCircleXmark = 0xE557, @@ -6564,14 +6464,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "plane-lock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "plane lock", "airplane", "airport", "closed", "flight", "fly", "lockdown", "padlock", "privacy", "quarantine", "travel" })] + [FontAwesomeSearchTerms(new[] { "plane lock", "airplane", "airport", "closed", "flight", "fly", "lockdown", "quarantine", "travel" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics", "Travel + Hotel" })] PlaneLock = 0xE558, /// <summary> /// The Font Awesome "plane-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "plane slash", "airplane mode", "airport", "canceled", "covid-19", "delayed", "disabled", "grounded", "travel" })] + [FontAwesomeSearchTerms(new[] { "plane slash", "airplane mode", "airport", "canceled", "covid-19", "delayed", "grounded", "travel" })] [FontAwesomeCategoriesAttribute(new[] { "Transportation", "Travel + Hotel" })] PlaneSlash = 0xE069, @@ -6627,21 +6527,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "plug-circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "plug circle check", "electric", "electricity", "enable", "not affected", "ok", "okay", "plug", "power", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "plug circle check", "electric", "electricity", "not affected", "ok", "okay", "plug", "power" })] [FontAwesomeCategoriesAttribute(new[] { "Energy", "Humanitarian" })] PlugCircleCheck = 0xE55C, /// <summary> /// The Font Awesome "plug-circle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "plug circle exclamation", "affected", "electric", "electricity", "failed", "plug", "power" })] + [FontAwesomeSearchTerms(new[] { "plug circle exclamation", "affected", "electric", "electricity", "plug", "power" })] [FontAwesomeCategoriesAttribute(new[] { "Energy", "Humanitarian" })] PlugCircleExclamation = 0xE55D, /// <summary> /// The Font Awesome "plug-circle-minus" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "plug circle minus", "disconnect", "electric", "electricity", "plug", "power" })] + [FontAwesomeSearchTerms(new[] { "plug circle minus", "electric", "electricity", "plug", "power" })] [FontAwesomeCategoriesAttribute(new[] { "Energy", "Humanitarian" })] PlugCircleMinus = 0xE55E, @@ -6655,7 +6555,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "plug-circle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "plug circle xmark", "destroy", "disconnect", "electric", "electricity", "outage", "plug", "power", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "plug circle xmark", "destroy", "electric", "electricity", "outage", "plug", "power" })] [FontAwesomeCategoriesAttribute(new[] { "Energy", "Humanitarian" })] PlugCircleXmark = 0xE560, @@ -6663,7 +6563,7 @@ public enum FontAwesomeIcon /// The Font Awesome "plus" icon unicode character. /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0x2B. /// </summary> - [FontAwesomeSearchTerms(new[] { "+", "plus sign", "add", "create", "expand", "follow", "math", "modify", "new", "plus", "positive", "shape", "sign" })] + [FontAwesomeSearchTerms(new[] { "+", "plus sign", "add", "create", "expand", "math", "new", "plus", "positive", "shape", "sign" })] [FontAwesomeCategoriesAttribute(new[] { "Editing", "Maps", "Mathematics", "Medical + Health", "Punctuation + Symbols" })] Plus = 0xF067, @@ -6698,21 +6598,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "square-poll-vertical" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "square poll vertical", "chart", "graph", "results", "revenue", "statistics", "survey", "trend", "vote", "voting" })] + [FontAwesomeSearchTerms(new[] { "square poll vertical", "chart", "graph", "results", "survey", "trend", "vote", "voting" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Charts + Diagrams", "Marketing", "Social" })] Poll = 0xF681, /// <summary> /// The Font Awesome "square-poll-horizontal" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "square poll horizontal", "chart", "graph", "results", "statistics", "survey", "trend", "vote", "voting" })] + [FontAwesomeSearchTerms(new[] { "square poll horizontal", "chart", "graph", "results", "survey", "trend", "vote", "voting" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Charts + Diagrams", "Marketing", "Social" })] PollH = 0xF682, /// <summary> /// The Font Awesome "poo" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "crap", "dung", "face", "monster", "pile of poo", "poo", "poop", "shit", "smile", "turd", "uer" })] + [FontAwesomeSearchTerms(new[] { "crap", "dung", "face", "monster", "pile of poo", "poo", "poop", "shit", "smile", "turd" })] [FontAwesomeCategoriesAttribute(new[] { "Communication", "Users + People" })] Poo = 0xF2FE, @@ -6733,7 +6633,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "image-portrait" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "image portrait", "id", "image", "img", "photo", "picture", "selfie", "uer", "username" })] + [FontAwesomeSearchTerms(new[] { "image portrait", "id", "image", "photo", "picture", "selfie" })] [FontAwesomeCategoriesAttribute(new[] { "Photos + Images", "Users + People" })] Portrait = 0xF3E0, @@ -6754,7 +6654,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "person-praying" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person praying", "kneel", "place of worship", "religion", "thank", "uer", "worship" })] + [FontAwesomeSearchTerms(new[] { "person praying", "kneel", "place of worship", "religion", "thank", "worship" })] [FontAwesomeCategoriesAttribute(new[] { "Religion", "Users + People" })] Pray = 0xF683, @@ -6803,7 +6703,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "diagram-project" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "diagram project", "chart", "graph", "network", "pert", "statistics" })] + [FontAwesomeSearchTerms(new[] { "diagram project", "chart", "graph", "network", "pert" })] [FontAwesomeCategoriesAttribute(new[] { "Charts + Diagrams", "Coding" })] ProjectDiagram = 0xF542, @@ -6831,22 +6731,22 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "qrcode" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "qrcode", "barcode", "info", "information", "qr", "qr-code", "scan" })] - [FontAwesomeCategoriesAttribute(new[] { "Coding", "Shopping" })] + [FontAwesomeSearchTerms(new[] { "qrcode", "barcode", "info", "information", "scan" })] + [FontAwesomeCategoriesAttribute(new[] { "Coding" })] Qrcode = 0xF029, /// <summary> /// The Font Awesome "question" icon unicode character. /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0x3F. /// </summary> - [FontAwesomeSearchTerms(new[] { "?", "question mark", "faq", "help", "information", "mark", "outlined", "punctuation", "question", "red question mark", "request", "support", "unknown", "white question mark" })] + [FontAwesomeSearchTerms(new[] { "?", "question mark", "help", "information", "mark", "outlined", "punctuation", "question", "red question mark", "support", "unknown", "white question mark" })] [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Alert", "Punctuation + Symbols" })] Question = 0xF128, /// <summary> /// The Font Awesome "circle-question" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "circle question", "faq", "help", "information", "support", "unknown" })] + [FontAwesomeSearchTerms(new[] { "circle question", "help", "information", "support", "unknown" })] [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Punctuation + Symbols" })] QuestionCircle = 0xF059, @@ -6916,14 +6816,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "ranking-star" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "ranking star", "chart", "first place", "podium", "quality", "rank", "revenue", "win" })] + [FontAwesomeSearchTerms(new[] { "ranking star", "chart", "first place", "podium", "rank", "win" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Marketing", "Sports + Fitness" })] RankingStar = 0xE561, /// <summary> /// The Font Awesome "receipt" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "accounting", "bookkeeping", "check", "coupon", "evidence", "invoice", "money", "pay", "proof", "receipt", "table" })] + [FontAwesomeSearchTerms(new[] { "accounting", "bookkeeping", "check", "evidence", "invoice", "money", "pay", "proof", "receipt", "table" })] [FontAwesomeCategoriesAttribute(new[] { "Medical + Health", "Money", "Shopping" })] Receipt = 0xF543, @@ -6944,15 +6844,15 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "arrow-rotate-right" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrow rotate right", "clockwise open circle arrow", "forward", "refresh", "reload", "renew", "repeat", "retry" })] - [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback", "Spinners" })] + [FontAwesomeSearchTerms(new[] { "arrow rotate right", "clockwise open circle arrow", "forward", "refresh", "reload", "repeat" })] + [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback" })] Redo = 0xF01E, /// <summary> /// The Font Awesome "rotate-right" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "rotate right", "forward", "refresh", "reload", "renew", "repeat", "retry" })] - [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback", "Spinners" })] + [FontAwesomeSearchTerms(new[] { "rotate right", "forward", "refresh", "reload", "repeat" })] + [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback" })] RedoAlt = 0xF2F9, /// <summary> @@ -6965,14 +6865,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "text-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "text slash", "cancel", "disabled", "font", "format", "remove", "style", "text" })] + [FontAwesomeSearchTerms(new[] { "text slash", "cancel", "font", "format", "remove", "style", "text" })] [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] RemoveFormat = 0xF87D, /// <summary> /// The Font Awesome "repeat" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrow", "clockwise", "flip", "reload", "renew", "repeat", "repeat button", "retry", "rewind", "switch" })] + [FontAwesomeSearchTerms(new[] { "arrow", "clockwise", "flip", "reload", "repeat", "repeat button", "rewind", "switch" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback" })] Repeat = 0xF363, @@ -7000,14 +6900,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "restroom" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "restroom", "bathroom", "toilet", "uer", "water closet", "wc" })] + [FontAwesomeSearchTerms(new[] { "restroom", "bathroom", "toilet", "water closet", "wc" })] [FontAwesomeCategoriesAttribute(new[] { "Maps", "Users + People" })] Restroom = 0xF7BD, /// <summary> /// The Font Awesome "retweet" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "retweet", "refresh", "reload", "renew", "retry", "share", "swap" })] + [FontAwesomeSearchTerms(new[] { "retweet", "refresh", "reload", "share", "swap" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Social" })] Retweet = 0xF079, @@ -7021,7 +6921,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "ring" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "ring", "dungeons & dragons", "gollum", "band", "binding", "d&d", "dnd", "engagement", "fantasy", "gold", "jewelry", "marriage", "precious", "premium" })] + [FontAwesomeSearchTerms(new[] { "ring", "dungeons & dragons", "gollum", "band", "binding", "d&d", "dnd", "engagement", "fantasy", "gold", "jewelry", "marriage", "precious" })] [FontAwesomeCategoriesAttribute(new[] { "Gaming", "Spinners" })] Ring = 0xF70B, @@ -7049,28 +6949,28 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "road-circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "road circle check", "enable", "freeway", "highway", "not affected", "ok", "okay", "pavement", "road", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "road circle check", "freeway", "highway", "not affected", "ok", "okay", "pavement", "road" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics" })] RoadCircleCheck = 0xE564, /// <summary> /// The Font Awesome "road-circle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "road circle exclamation", "affected", "failed", "freeway", "highway", "pavement", "road" })] + [FontAwesomeSearchTerms(new[] { "road circle exclamation", "affected", "freeway", "highway", "pavement", "road" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics" })] RoadCircleExclamation = 0xE565, /// <summary> /// The Font Awesome "road-circle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "road circle xmark", "destroy", "freeway", "highway", "pavement", "road", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "road circle xmark", "destroy", "freeway", "highway", "pavement", "road" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics" })] RoadCircleXmark = 0xE566, /// <summary> /// The Font Awesome "road-lock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "road lock", "closed", "freeway", "highway", "lockdown", "padlock", "pavement", "privacy", "quarantine", "road" })] + [FontAwesomeSearchTerms(new[] { "road lock", "closed", "freeway", "highway", "lockdown", "pavement", "quarantine", "road" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Logistics" })] RoadLock = 0xE567, @@ -7161,7 +7061,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "person-running" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person running", "exit", "flee", "follow", "marathon", "person running", "race", "running", "uer", "workout" })] + [FontAwesomeSearchTerms(new[] { "person running", "exit", "flee", "marathon", "person running", "race", "running" })] [FontAwesomeCategoriesAttribute(new[] { "Sports + Fitness", "Users + People" })] Running = 0xF70C, @@ -7182,14 +7082,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "sack-dollar" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "sack dollar", "bag", "burlap", "cash", "dollar", "investment", "money", "money bag", "moneybag", "premium", "robber", "salary", "santa", "usd" })] + [FontAwesomeSearchTerms(new[] { "sack dollar", "bag", "burlap", "cash", "dollar", "money", "money bag", "moneybag", "robber", "santa", "usd" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Money" })] SackDollar = 0xF81D, /// <summary> /// The Font Awesome "sack-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "sack xmark", "bag", "burlap", "coupon", "rations", "salary", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "sack xmark", "bag", "burlap", "rations" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Money" })] SackXmark = 0xE56A, @@ -7245,21 +7145,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "school-circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "school circle check", "enable", "not affected", "ok", "okay", "schoolhouse", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "school circle check", "not affected", "ok", "okay", "schoolhouse" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Education", "Humanitarian" })] SchoolCircleCheck = 0xE56B, /// <summary> /// The Font Awesome "school-circle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "school circle exclamation", "affected", "failed", "schoolhouse" })] + [FontAwesomeSearchTerms(new[] { "school circle exclamation", "affected", "schoolhouse" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Education", "Humanitarian" })] SchoolCircleExclamation = 0xE56C, /// <summary> /// The Font Awesome "school-circle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "school circle xmark", "destroy", "schoolhouse", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "school circle xmark", "destroy", "schoolhouse" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Education", "Humanitarian" })] SchoolCircleXmark = 0xE56D, @@ -7273,63 +7173,63 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "school-lock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "school lock", "closed", "lockdown", "padlock", "privacy", "quarantine", "schoolhouse" })] + [FontAwesomeSearchTerms(new[] { "school lock", "closed", "lockdown", "quarantine", "schoolhouse" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Education", "Humanitarian" })] SchoolLock = 0xE56F, /// <summary> /// The Font Awesome "screwdriver" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "admin", "configuration", "equipment", "fix", "maintenance", "mechanic", "modify", "repair", "screw", "screwdriver", "settings", "tool" })] + [FontAwesomeSearchTerms(new[] { "admin", "fix", "mechanic", "repair", "screw", "screwdriver", "settings", "tool" })] [FontAwesomeCategoriesAttribute(new[] { "Construction" })] Screwdriver = 0xF54A, /// <summary> /// The Font Awesome "scroll" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "dungeons & dragons", "announcement", "d&d", "dnd", "fantasy", "paper", "scholar", "script", "scroll" })] + [FontAwesomeSearchTerms(new[] { "dungeons & dragons", "announcement", "d&d", "dnd", "fantasy", "paper", "script", "scroll" })] [FontAwesomeCategoriesAttribute(new[] { "Gaming" })] Scroll = 0xF70E, /// <summary> /// The Font Awesome "sd-card" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "sd card", "image", "img", "memory", "photo", "save" })] + [FontAwesomeSearchTerms(new[] { "sd card", "image", "memory", "photo", "save" })] [FontAwesomeCategoriesAttribute(new[] { "Devices + Hardware" })] SdCard = 0xF7C2, /// <summary> /// The Font Awesome "magnifying-glass" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "magnifying glass", "bigger", "enlarge", "equipment", "find", "glass", "inspection", "magnifier", "magnify", "magnifying", "magnifying glass tilted left", "preview", "search", "tool", "zoom" })] + [FontAwesomeSearchTerms(new[] { "magnifying glass", "bigger", "enlarge", "find", "glass", "magnify", "magnifying", "magnifying glass tilted left", "preview", "search", "tool", "zoom" })] [FontAwesomeCategoriesAttribute(new[] { "Maps" })] Search = 0xF002, /// <summary> /// The Font Awesome "magnifying-glass-dollar" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "magnifying glass dollar", "bigger", "enlarge", "find", "magnifier", "magnify", "money", "preview", "zoom" })] + [FontAwesomeSearchTerms(new[] { "magnifying glass dollar", "bigger", "enlarge", "find", "magnify", "money", "preview", "zoom" })] [FontAwesomeCategoriesAttribute(new[] { "Marketing" })] SearchDollar = 0xF688, /// <summary> /// The Font Awesome "magnifying-glass-location" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "magnifying glass location", "bigger", "enlarge", "find", "magnifier", "magnify", "preview", "zoom" })] - [FontAwesomeCategoriesAttribute(new[] { "Maps", "Marketing" })] + [FontAwesomeSearchTerms(new[] { "magnifying glass location", "bigger", "enlarge", "find", "magnify", "preview", "zoom" })] + [FontAwesomeCategoriesAttribute(new[] { "Marketing" })] SearchLocation = 0xF689, /// <summary> /// The Font Awesome "magnifying-glass-minus" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "magnifying glass minus", "magnifier", "minify", "negative", "smaller", "zoom", "zoom out" })] + [FontAwesomeSearchTerms(new[] { "magnifying glass minus", "minify", "negative", "smaller", "zoom", "zoom out" })] [FontAwesomeCategoriesAttribute(new[] { "Maps" })] SearchMinus = 0xF010, /// <summary> /// The Font Awesome "magnifying-glass-plus" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "magnifying glass plus", "bigger", "enlarge", "magnifier", "magnify", "positive", "zoom", "zoom in" })] + [FontAwesomeSearchTerms(new[] { "magnifying glass plus", "bigger", "enlarge", "magnify", "positive", "zoom", "zoom in" })] [FontAwesomeCategoriesAttribute(new[] { "Maps" })] SearchPlus = 0xF00E, @@ -7343,21 +7243,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "seedling" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "environment", "flora", "grow", "investment", "plant", "sapling", "seedling", "vegan", "young" })] + [FontAwesomeSearchTerms(new[] { "environment", "flora", "grow", "plant", "sapling", "seedling", "vegan", "young" })] [FontAwesomeCategoriesAttribute(new[] { "Charity", "Energy", "Food + Beverage", "Fruits + Vegetables", "Humanitarian", "Nature", "Science" })] Seedling = 0xF4D8, - /// <summary> - /// The Font Awesome "septagon" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "septagon", "7", "heptagon", "seven", "shape" })] - [FontAwesomeCategoriesAttribute(new[] { "Shapes" })] - Septagon = 0xE820, - /// <summary> /// The Font Awesome "server" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "server", "computer", "cpu", "database", "hardware", "mysql", "network", "sql" })] + [FontAwesomeSearchTerms(new[] { "server", "computer", "cpu", "database", "hardware", "network" })] [FontAwesomeCategoriesAttribute(new[] { "Devices + Hardware" })] Server = 0xF233, @@ -7420,7 +7313,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "shield-halved" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "shield halved", "achievement", "armor", "award", "block", "cleric", "defend", "defense", "holy", "paladin", "privacy", "security", "shield", "weapon", "winner" })] + [FontAwesomeSearchTerms(new[] { "shield halved", "achievement", "armor", "award", "block", "cleric", "defend", "defense", "holy", "paladin", "security", "shield", "weapon", "winner" })] [FontAwesomeCategoriesAttribute(new[] { "Coding", "Gaming", "Security" })] ShieldAlt = 0xF3ED, @@ -7441,7 +7334,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "shield-heart" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "shield heart", "love", "protect", "safe", "safety", "shield", "wishlist" })] + [FontAwesomeSearchTerms(new[] { "shield heart", "love", "protect", "safe", "safety", "shield" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Security" })] ShieldHeart = 0xE574, @@ -7462,7 +7355,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "truck-fast" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "truck fast", "express", "fedex", "mail", "overnight", "package", "quick", "ups" })] + [FontAwesomeSearchTerms(new[] { "truck fast", "express", "fedex", "mail", "overnight", "package", "ups" })] [FontAwesomeCategoriesAttribute(new[] { "Logistics", "Shopping" })] ShippingFast = 0xF48B, @@ -7476,7 +7369,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "shop-lock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "shop lock", "bodega", "building", "buy", "closed", "lock", "lockdown", "market", "padlock", "privacy", "purchase", "quarantine", "shop", "shopping", "store" })] + [FontAwesomeSearchTerms(new[] { "shop lock", "bodega", "building", "buy", "closed", "lock", "lockdown", "market", "purchase", "quarantine", "shop", "shopping", "store" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Humanitarian", "Shopping" })] ShopLock = 0xE4A5, @@ -7504,7 +7397,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "shop-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "shop slash", "building", "buy", "closed", "disabled", "purchase", "shopping" })] + [FontAwesomeSearchTerms(new[] { "shop slash", "building", "buy", "closed", "covid-19", "purchase", "shopping" })] [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] ShopSlash = 0xE070, @@ -7525,7 +7418,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "van-shuttle" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "van shuttle", "airport", "bus", "minibus", "public-transportation", "transportation", "travel", "vehicle" })] + [FontAwesomeSearchTerms(new[] { "van shuttle", "airport", "bus", "machine", "minibus", "public-transportation", "transportation", "travel", "vehicle" })] [FontAwesomeCategoriesAttribute(new[] { "Automotive", "Transportation", "Travel + Hotel" })] ShuttleVan = 0xF5B6, @@ -7546,7 +7439,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "signature" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "signature", "john hancock", "cursive", "name", "username", "writing" })] + [FontAwesomeSearchTerms(new[] { "signature", "john hancock", "cursive", "name", "writing" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Editing", "Writing" })] Signature = 0xF5B7, @@ -7578,20 +7471,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Devices + Hardware" })] SimCard = 0xF7C4, - /// <summary> - /// The Font Awesome "single-quote-left" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "single quote left", "left single quotation mark", "mention", "note", "phrase", "text", "type" })] - [FontAwesomeCategoriesAttribute(new[] { "Communication", "Punctuation + Symbols", "Writing" })] - SingleQuoteLeft = 0xE81B, - - /// <summary> - /// The Font Awesome "single-quote-right" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "single quote right", "mention", "note", "phrase", "right single quotation mark", "text", "type" })] - [FontAwesomeCategoriesAttribute(new[] { "Communication", "Punctuation + Symbols", "Writing" })] - SingleQuoteRight = 0xE81C, - /// <summary> /// The Font Awesome "sink" icon unicode character. /// </summary> @@ -7609,28 +7488,28 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "person-skating" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person skating", "figure skating", "ice", "olympics", "rink", "skate", "uer", "winter" })] + [FontAwesomeSearchTerms(new[] { "person skating", "figure skating", "ice", "olympics", "rink", "skate", "winter" })] [FontAwesomeCategoriesAttribute(new[] { "Sports + Fitness", "Users + People" })] Skating = 0xF7C5, /// <summary> /// The Font Awesome "person-skiing" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person skiing", "downhill", "olympics", "ski", "skier", "snow", "uer", "winter" })] + [FontAwesomeSearchTerms(new[] { "person skiing", "downhill", "olympics", "ski", "skier", "snow", "winter" })] [FontAwesomeCategoriesAttribute(new[] { "Sports + Fitness", "Users + People" })] Skiing = 0xF7C9, /// <summary> /// The Font Awesome "person-skiing-nordic" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person skiing nordic", "cross country", "olympics", "uer", "winter" })] + [FontAwesomeSearchTerms(new[] { "person skiing nordic", "cross country", "olympics", "winter" })] [FontAwesomeCategoriesAttribute(new[] { "Sports + Fitness", "Users + People" })] SkiingNordic = 0xF7CA, /// <summary> /// The Font Awesome "skull" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "bones", "death", "face", "fairy tale", "monster", "skeleton", "skull", "uer", "x-ray", "yorick" })] + [FontAwesomeSearchTerms(new[] { "bones", "death", "face", "fairy tale", "monster", "skeleton", "skull", "x-ray", "yorick" })] [FontAwesomeCategoriesAttribute(new[] { "Halloween", "Medical + Health", "Users + People" })] Skull = 0xF54C, @@ -7658,14 +7537,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "sliders" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "adjust", "configuration", "modify", "settings", "sliders", "toggle" })] - [FontAwesomeCategoriesAttribute(new[] { "Editing", "Media Playback", "Music + Audio", "Photos + Images", "Toggle" })] + [FontAwesomeSearchTerms(new[] { "adjust", "settings", "sliders", "toggle" })] + [FontAwesomeCategoriesAttribute(new[] { "Editing", "Media Playback", "Music + Audio", "Photos + Images" })] SlidersH = 0xF1DE, /// <summary> /// The Font Awesome "face-smile" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "face smile", "approve", "default", "emoticon", "face", "happy", "rating", "satisfied", "slightly smiling face", "smile", "uer" })] + [FontAwesomeSearchTerms(new[] { "face smile", "approve", "emoticon", "face", "happy", "rating", "satisfied", "slightly smiling face", "smile" })] [FontAwesomeCategoriesAttribute(new[] { "Communication", "Emoji", "Users + People" })] Smile = 0xF118, @@ -7700,21 +7579,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "ban-smoking" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "ban smoking", "ban", "cancel", "circle", "deny", "disabled", "forbidden", "no", "no smoking", "non-smoking", "not", "prohibited", "slash", "smoking" })] + [FontAwesomeSearchTerms(new[] { "ban smoking", "ban", "cancel", "forbidden", "no", "no smoking", "non-smoking", "not", "prohibited", "smoking" })] [FontAwesomeCategoriesAttribute(new[] { "Medical + Health", "Travel + Hotel" })] SmokingBan = 0xF54D, /// <summary> /// The Font Awesome "comment-sms" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "comment sms", "answer", "chat", "conversation", "message", "mobile", "notification", "phone", "sms", "texting" })] + [FontAwesomeSearchTerms(new[] { "comment sms", "chat", "conversation", "message", "mobile", "notification", "phone", "sms", "texting" })] [FontAwesomeCategoriesAttribute(new[] { "Communication" })] Sms = 0xF7CD, /// <summary> /// The Font Awesome "person-snowboarding" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person snowboarding", "olympics", "ski", "snow", "snowboard", "snowboarder", "uer", "winter" })] + [FontAwesomeSearchTerms(new[] { "person snowboarding", "olympics", "ski", "snow", "snowboard", "snowboarder", "winter" })] [FontAwesomeCategoriesAttribute(new[] { "Sports + Fitness", "Users + People" })] Snowboarding = 0xF7CE, @@ -7812,7 +7691,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "arrow-up-wide-short" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrow up wide short", "arrange", "filter", "order", "sort-amount-desc", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "arrow up wide short", "arrange", "filter", "order", "sort-amount-desc" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] SortAmountUp = 0xF161, @@ -7826,7 +7705,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "sort-down" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "sort down", "arrow", "descending", "filter", "insert", "order", "sort-desc" })] + [FontAwesomeSearchTerms(new[] { "sort down", "arrow", "descending", "filter", "order", "sort-desc" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] SortDown = 0xF0DD, @@ -7861,7 +7740,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "sort-up" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "sort up", "arrow", "ascending", "filter", "order", "sort-asc", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "sort up", "arrow", "ascending", "filter", "order", "sort-asc" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] SortUp = 0xF0DE, @@ -7882,7 +7761,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "spell-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "spell check", "dictionary", "edit", "editor", "enable", "grammar", "text", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "spell check", "dictionary", "edit", "editor", "grammar", "text" })] [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] SpellCheck = 0xF891, @@ -7896,17 +7775,10 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "spinner" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "spinner", "circle", "loading", "pending", "progress" })] + [FontAwesomeSearchTerms(new[] { "spinner", "circle", "loading", "progress" })] [FontAwesomeCategoriesAttribute(new[] { "Spinners" })] Spinner = 0xF110, - /// <summary> - /// The Font Awesome "spiral" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "spiral", "design", "dizzy", "rotate", "spin", "swirl", "twist" })] - [FontAwesomeCategoriesAttribute(new[] { "Design", "Shapes" })] - Spiral = 0xE80A, - /// <summary> /// The Font Awesome "splotch" icon unicode character. /// </summary> @@ -7935,13 +7807,6 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Arrows" })] SquareArrowUpRight = 0xF14C, - /// <summary> - /// The Font Awesome "square-binary" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "square binary", "ai", "data", "language", "llm", "model", "programming", "token" })] - [FontAwesomeCategoriesAttribute(new[] { "Coding", "Shapes" })] - SquareBinary = 0xE69B, - /// <summary> /// The Font Awesome "square-full" icon unicode character. /// </summary> @@ -7959,7 +7824,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "square-person-confined" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "square person confined", "captivity", "confined", "uer" })] + [FontAwesomeSearchTerms(new[] { "square person confined", "captivity", "confined" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Security", "Users + People" })] SquarePersonConfined = 0xE577, @@ -7980,7 +7845,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "square-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "square xmark", "close", "cross", "cross mark button", "incorrect", "mark", "notice", "notification", "notify", "problem", "square", "uncheck", "window", "wrong", "x", "×" })] + [FontAwesomeSearchTerms(new[] { "square xmark", "close", "cross", "cross mark button", "incorrect", "mark", "notice", "notification", "notify", "problem", "square", "window", "wrong", "x", "×" })] [FontAwesomeCategoriesAttribute(new[] { "Mathematics" })] SquareXmark = 0xF2D3, @@ -8015,7 +7880,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "star" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "achievement", "award", "favorite", "important", "night", "quality", "rating", "score", "star", "vip" })] + [FontAwesomeSearchTerms(new[] { "achievement", "award", "favorite", "important", "night", "rating", "score", "star" })] [FontAwesomeCategoriesAttribute(new[] { "Shapes", "Shopping", "Social", "Toggle" })] Star = 0xF005, @@ -8099,7 +7964,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "stopwatch" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "clock", "reminder", "stopwatch", "time", "waiting" })] + [FontAwesomeSearchTerms(new[] { "clock", "reminder", "stopwatch", "time" })] [FontAwesomeCategoriesAttribute(new[] { "Time" })] Stopwatch = 0xF2F2, @@ -8127,7 +7992,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "store-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "store slash", "building", "buy", "closed", "disabled", "purchase", "shopping" })] + [FontAwesomeSearchTerms(new[] { "store slash", "building", "buy", "closed", "covid-19", "purchase", "shopping" })] [FontAwesomeCategoriesAttribute(new[] { "Shopping" })] StoreSlash = 0xE071, @@ -8141,14 +8006,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "street-view" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "street view", "directions", "location", "map", "navigation", "uer" })] + [FontAwesomeSearchTerms(new[] { "street view", "directions", "location", "map", "navigation" })] [FontAwesomeCategoriesAttribute(new[] { "Maps", "Users + People" })] StreetView = 0xF21D, /// <summary> /// The Font Awesome "strikethrough" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "strikethrough", "cancel", "edit", "font", "format", "modify", "text", "type" })] + [FontAwesomeSearchTerms(new[] { "strikethrough", "cancel", "edit", "font", "format", "text", "type" })] [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] Strikethrough = 0xF0CC, @@ -8225,7 +8090,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "person-swimming" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person swimming", "ocean", "person swimming", "pool", "sea", "swim", "uer", "water" })] + [FontAwesomeSearchTerms(new[] { "person swimming", "ocean", "person swimming", "pool", "sea", "swim", "water" })] [FontAwesomeCategoriesAttribute(new[] { "Maritime", "Sports + Fitness", "Travel + Hotel", "Users + People" })] Swimmer = 0xF5C4, @@ -8246,14 +8111,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "arrows-rotate" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrows rotate", "clockwise right and left semicircle arrows", "clockwise", "exchange", "modify", "refresh", "reload", "renew", "retry", "rotate", "swap" })] - [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Editing", "Media Playback", "Spinners" })] + [FontAwesomeSearchTerms(new[] { "arrows rotate", "clockwise right and left semicircle arrows", "exchange", "refresh", "reload", "rotate", "swap" })] + [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Editing", "Media Playback" })] Sync = 0xF021, /// <summary> /// The Font Awesome "rotate" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "arrow", "clockwise", "exchange", "modify", "refresh", "reload", "renew", "retry", "rotate", "swap", "withershins" })] + [FontAwesomeSearchTerms(new[] { "anticlockwise", "arrow", "counterclockwise", "counterclockwise arrows button", "exchange", "refresh", "reload", "rotate", "swap", "withershins" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Editing", "Media Playback", "Spinners" })] SyncAlt = 0xF2F1, @@ -8267,31 +8132,10 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "table" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "table", "category", "data", "excel", "spreadsheet" })] + [FontAwesomeSearchTerms(new[] { "table", "data", "excel", "spreadsheet" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Text Formatting" })] Table = 0xF0CE, - /// <summary> - /// The Font Awesome "table-cells-column-lock" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "table cells column lock", "blocks", "boxes", "category", "column", "excel", "grid", "lock", "spreadsheet", "squares" })] - [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] - TableCellsColumnLock = 0xE678, - - /// <summary> - /// The Font Awesome "table-cells-row-lock" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "table cells row lock", "blocks", "boxes", "category", "column", "column", "excel", "grid", "lock", "lock", "spreadsheet", "squares" })] - [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] - TableCellsRowLock = 0xE67A, - - /// <summary> - /// The Font Awesome "table-cells-row-unlock" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "table cells row unlock", "blocks", "boxes", "category", "column", "column", "excel", "grid", "lock", "lock", "spreadsheet", "squares", "unlock" })] - [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] - TableCellsRowUnlock = 0xE691, - /// <summary> /// The Font Awesome "tablet" icon unicode character. /// </summary> @@ -8331,7 +8175,7 @@ public enum FontAwesomeIcon /// The Font Awesome "gauge-high" icon unicode character. /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0xF625. /// </summary> - [FontAwesomeSearchTerms(new[] { "gauge high", "dashboard", "fast", "odometer", "quick", "speed", "speedometer" })] + [FontAwesomeSearchTerms(new[] { "gauge high", "dashboard", "fast", "odometer", "speed", "speedometer" })] [FontAwesomeCategoriesAttribute(new[] { "Automotive" })] TachometerAlt = 0xF3FD, @@ -8373,7 +8217,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "list-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "list check", "bullet", "cheatsheet", "checklist", "downloading", "downloads", "enable", "loading", "progress", "project management", "settings", "summary", "to do", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "list check", "checklist", "downloading", "downloads", "loading", "progress", "project management", "settings", "to do" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Text Formatting" })] Tasks = 0xF0AE, @@ -8436,42 +8280,42 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "tent" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "bivouac", "campground", "campsite", "refugee", "shelter", "tent" })] + [FontAwesomeSearchTerms(new[] { "bivouac", "campground", "refugee", "shelter", "tent" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Camping", "Humanitarian" })] Tent = 0xE57D, /// <summary> /// The Font Awesome "tent-arrow-down-to-line" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "tent arrow down to line", "bivouac", "campground", "campsite", "permanent", "refugee", "refugee", "shelter", "shelter", "tent" })] + [FontAwesomeSearchTerms(new[] { "tent arrow down to line", "permanent", "refugee", "shelter" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Camping", "Humanitarian" })] TentArrowDownToLine = 0xE57E, /// <summary> /// The Font Awesome "tent-arrow-left-right" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "tent arrow left right", "bivouac", "campground", "campsite", "refugee", "refugee", "shelter", "shelter", "tent", "transition" })] + [FontAwesomeSearchTerms(new[] { "tent arrow left right", "refugee", "shelter", "transition" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Camping", "Humanitarian" })] TentArrowLeftRight = 0xE57F, /// <summary> /// The Font Awesome "tent-arrows-down" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "tent arrows down", "bivouac", "campground", "campsite", "insert", "refugee", "refugee", "shelter", "shelter", "spontaneous", "tent" })] + [FontAwesomeSearchTerms(new[] { "tent arrows down", "refugee", "shelter", "spontaneous" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Camping", "Humanitarian" })] TentArrowsDown = 0xE581, /// <summary> /// The Font Awesome "tent-arrow-turn-left" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "tent arrow turn left", "bivouac", "campground", "campsite", "refugee", "refugee", "shelter", "shelter", "temporary", "tent" })] + [FontAwesomeSearchTerms(new[] { "tent arrow turn left", "refugee", "shelter", "temporary" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Camping", "Humanitarian" })] TentArrowTurnLeft = 0xE580, /// <summary> /// The Font Awesome "tents" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "tents", "bivouac", "bivouac", "campground", "campground", "campsite", "refugee", "refugee", "shelter", "shelter", "tent", "tent" })] + [FontAwesomeSearchTerms(new[] { "tents", "bivouac", "campground", "refugee", "shelter", "tent" })] [FontAwesomeCategoriesAttribute(new[] { "Buildings", "Camping", "Humanitarian" })] Tents = 0xE582, @@ -8485,21 +8329,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "text-height" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "text height", "edit", "font", "format", "modify", "text", "type" })] + [FontAwesomeSearchTerms(new[] { "text height", "edit", "font", "format", "text", "type" })] [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] TextHeight = 0xF034, /// <summary> /// The Font Awesome "text-width" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "text width", "edit", "font", "format", "modify", "text", "type" })] + [FontAwesomeSearchTerms(new[] { "text width", "edit", "font", "format", "text", "type" })] [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] TextWidth = 0xF035, /// <summary> /// The Font Awesome "table-cells" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "table cells", "blocks", "boxes", "category", "excel", "grid", "spreadsheet", "squares" })] + [FontAwesomeSearchTerms(new[] { "table cells", "blocks", "boxes", "grid", "squares" })] [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] Th = 0xF00A, @@ -8555,14 +8399,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "table-cells-large" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "table cells large", "blocks", "boxes", "category", "excel", "grid", "spreadsheet", "squares" })] + [FontAwesomeSearchTerms(new[] { "table cells large", "blocks", "boxes", "grid", "squares" })] [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] ThLarge = 0xF009, /// <summary> /// The Font Awesome "table-list" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "table list", "category", "cheatsheet", "checklist", "completed", "done", "finished", "ol", "summary", "todo", "ul" })] + [FontAwesomeSearchTerms(new[] { "table list", "checklist", "completed", "done", "finished", "ol", "todo", "ul" })] [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] ThList = 0xF00B, @@ -8587,24 +8431,17 @@ public enum FontAwesomeIcon [FontAwesomeCategoriesAttribute(new[] { "Business", "Maps", "Social", "Writing" })] Thumbtack = 0xF08D, - /// <summary> - /// The Font Awesome "thumbtack-slash" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "thumbtack slash", "black pushpin", "coordinates", "location", "marker", "pin", "pushpin", "thumb-tack", "unpin" })] - [FontAwesomeCategoriesAttribute(new[] { "Business", "Maps", "Social", "Writing" })] - ThumbtackSlash = 0xE68F, - /// <summary> /// The Font Awesome "ticket" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "admission", "admission tickets", "coupon", "movie", "pass", "support", "ticket", "voucher" })] + [FontAwesomeSearchTerms(new[] { "admission", "admission tickets", "movie", "pass", "support", "ticket" })] [FontAwesomeCategoriesAttribute(new[] { "Film + Video", "Maps" })] Ticket = 0xF145, /// <summary> /// The Font Awesome "ticket-simple" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "ticket simple", "admission", "coupon", "movie", "pass", "support", "ticket", "voucher" })] + [FontAwesomeSearchTerms(new[] { "ticket simple", "movie", "pass", "support", "ticket" })] [FontAwesomeCategoriesAttribute(new[] { "Maps", "Shapes" })] TicketAlt = 0xF3FF, @@ -8618,29 +8455,29 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "xmark", "cancellation x", "multiplication sign", "multiplication x", "cancel", "close", "cross", "cross mark", "error", "exit", "incorrect", "mark", "multiplication", "multiply", "notice", "notification", "notify", "problem", "sign", "uncheck", "wrong", "x", "×" })] + [FontAwesomeSearchTerms(new[] { "xmark", "cancellation x", "multiplication sign", "multiplication x", "cancel", "close", "cross", "cross mark", "error", "exit", "incorrect", "mark", "multiplication", "multiply", "notice", "notification", "notify", "problem", "sign", "wrong", "x", "×" })] [FontAwesomeCategoriesAttribute(new[] { "Editing", "Mathematics" })] Times = 0xF00D, /// <summary> /// The Font Awesome "circle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "circle xmark", "close", "cross", "destroy", "exit", "incorrect", "notice", "notification", "notify", "problem", "uncheck", "wrong", "x" })] + [FontAwesomeSearchTerms(new[] { "circle xmark", "close", "cross", "destroy", "exit", "incorrect", "notice", "notification", "notify", "problem", "wrong", "x" })] [FontAwesomeCategoriesAttribute(new[] { "Mathematics" })] TimesCircle = 0xF057, /// <summary> /// The Font Awesome "droplet" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "blood", "cold", "color", "comic", "drop", "droplet", "raindrop", "sweat", "waterdrop" })] - [FontAwesomeCategoriesAttribute(new[] { "Design", "Humanitarian", "Maps", "Medical + Health", "Photos + Images" })] + [FontAwesomeSearchTerms(new[] { "cold", "color", "comic", "drop", "droplet", "raindrop", "sweat", "waterdrop" })] + [FontAwesomeCategoriesAttribute(new[] { "Design", "Humanitarian", "Maps", "Photos + Images" })] Tint = 0xF043, /// <summary> /// The Font Awesome "droplet-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "droplet slash", "blood", "color", "disabled", "drop", "droplet", "raindrop", "waterdrop" })] - [FontAwesomeCategoriesAttribute(new[] { "Design", "Medical + Health" })] + [FontAwesomeSearchTerms(new[] { "droplet slash", "color", "drop", "droplet", "raindrop", "waterdrop" })] + [FontAwesomeCategoriesAttribute(new[] { "Design" })] TintSlash = 0xF5C7, /// <summary> @@ -8681,7 +8518,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "toilet-paper-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "toilet paper slash", "bathroom", "covid-19", "disabled", "halloween", "holiday", "lavatory", "leaves", "prank", "privy", "restroom", "roll", "toilet", "trouble", "ut oh", "wipe" })] + [FontAwesomeSearchTerms(new[] { "toilet paper slash", "bathroom", "covid-19", "halloween", "holiday", "lavatory", "leaves", "prank", "privy", "restroom", "roll", "toilet", "trouble", "ut oh", "wipe" })] [FontAwesomeCategoriesAttribute(new[] { "Household" })] ToiletPaperSlash = 0xE072, @@ -8702,14 +8539,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "toolbox" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "admin", "chest", "configuration", "container", "equipment", "fix", "maintenance", "mechanic", "modify", "repair", "settings", "tool", "toolbox", "tools" })] + [FontAwesomeSearchTerms(new[] { "admin", "chest", "container", "fix", "mechanic", "repair", "settings", "tool", "toolbox", "tools" })] [FontAwesomeCategoriesAttribute(new[] { "Construction" })] Toolbox = 0xF552, /// <summary> /// The Font Awesome "screwdriver-wrench" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "screwdriver wrench", "admin", "configuration", "equipment", "fix", "maintenance", "modify", "repair", "screwdriver", "settings", "tools", "wrench" })] + [FontAwesomeSearchTerms(new[] { "screwdriver wrench", "admin", "fix", "repair", "screwdriver", "settings", "tools", "wrench" })] [FontAwesomeCategoriesAttribute(new[] { "Construction" })] Tools = 0xF7D9, @@ -8744,7 +8581,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "tower-cell" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "tower cell", "airwaves", "antenna", "communication", "radio", "reception", "signal", "waves" })] + [FontAwesomeSearchTerms(new[] { "tower cell", "airwaves", "antenna", "communication", "radio", "reception", "waves" })] [FontAwesomeCategoriesAttribute(new[] { "Communication", "Connectivity", "Film + Video", "Humanitarian" })] TowerCell = 0xE585, @@ -8772,7 +8609,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "traffic-light" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "traffic light", "direction", "go", "light", "road", "signal", "slow", "stop", "traffic", "travel", "vertical traffic light" })] + [FontAwesomeSearchTerms(new[] { "traffic light", "direction", "light", "road", "signal", "traffic", "travel", "vertical traffic light" })] [FontAwesomeCategoriesAttribute(new[] { "Maps" })] TrafficLight = 0xF637, @@ -8828,21 +8665,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "trash-arrow-up" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "trash arrow up", "back", "control z", "delete", "garbage", "hide", "oops", "remove", "undo", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "trash arrow up", "back", "control z", "delete", "garbage", "hide", "oops", "remove", "undo" })] [FontAwesomeCategoriesAttribute(new[] { "Editing" })] TrashRestore = 0xF829, /// <summary> /// The Font Awesome "trash-can-arrow-up" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "trash can arrow up", "back", "control z", "delete", "garbage", "hide", "oops", "remove", "undo", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "trash can arrow up", "back", "control z", "delete", "garbage", "hide", "oops", "remove", "undo" })] [FontAwesomeCategoriesAttribute(new[] { "Editing" })] TrashRestoreAlt = 0xF82A, /// <summary> /// The Font Awesome "tree" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "bark", "evergreen tree", "fall", "flora", "forest", "investment", "nature", "plant", "seasonal", "tree" })] + [FontAwesomeSearchTerms(new[] { "bark", "evergreen tree", "fall", "flora", "forest", "nature", "plant", "seasonal", "tree" })] [FontAwesomeCategoriesAttribute(new[] { "Camping", "Maps", "Nature" })] Tree = 0xF1BB, @@ -8863,14 +8700,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "trowel" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "trowel", "build", "construction", "equipment", "maintenance", "tool" })] + [FontAwesomeSearchTerms(new[] { "trowel", "build", "construction", "tool" })] [FontAwesomeCategoriesAttribute(new[] { "Construction", "Humanitarian" })] Trowel = 0xE589, /// <summary> /// The Font Awesome "trowel-bricks" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "trowel bricks", "build", "construction", "maintenance", "reconstruction", "tool" })] + [FontAwesomeSearchTerms(new[] { "trowel bricks", "build", "construction", "reconstruction", "tool" })] [FontAwesomeCategoriesAttribute(new[] { "Construction", "Humanitarian" })] TrowelBricks = 0xE58A, @@ -8891,8 +8728,8 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "truck-droplet" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "truck droplet", "blood", "thirst", "truck", "water", "water supply" })] - [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Medical + Health", "Transportation" })] + [FontAwesomeSearchTerms(new[] { "truck droplet", "thirst", "truck", "water", "water supply" })] + [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Transportation" })] TruckDroplet = 0xE58C, /// <summary> @@ -8940,7 +8777,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "truck-pickup" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "truck pickup", "cargo", "maintenance", "pick-up", "pickup", "pickup truck", "truck", "vehicle" })] + [FontAwesomeSearchTerms(new[] { "truck pickup", "cargo", "pick-up", "pickup", "pickup truck", "truck", "vehicle" })] [FontAwesomeCategoriesAttribute(new[] { "Automotive", "Construction", "Transportation" })] TruckPickup = 0xF63C, @@ -8996,7 +8833,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "underline" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "underline", "edit", "emphasis", "format", "modify", "text", "writing" })] + [FontAwesomeSearchTerms(new[] { "underline", "edit", "emphasis", "format", "text", "writing" })] [FontAwesomeCategoriesAttribute(new[] { "Text Formatting" })] Underline = 0xF0CD, @@ -9004,20 +8841,20 @@ public enum FontAwesomeIcon /// The Font Awesome "arrow-rotate-left" icon unicode character. /// </summary> [FontAwesomeSearchTerms(new[] { "arrow rotate left", "anticlockwise open circle arrow", "back", "control z", "exchange", "oops", "return", "rotate", "swap" })] - [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback", "Spinners" })] + [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback" })] Undo = 0xF0E2, /// <summary> /// The Font Awesome "rotate-left" icon unicode character. /// </summary> [FontAwesomeSearchTerms(new[] { "rotate left", "back", "control z", "exchange", "oops", "return", "swap" })] - [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback", "Spinners" })] + [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Media Playback" })] UndoAlt = 0xF2EA, /// <summary> /// The Font Awesome "universal-access" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "universal access", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "universal access", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Accessibility" })] UniversalAccess = 0xF29A, @@ -9031,254 +8868,252 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "link-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "link slash", "attachment", "chain", "chain-broken", "disabled", "disconnect", "remove" })] + [FontAwesomeSearchTerms(new[] { "link slash", "attachment", "chain", "chain-broken", "remove" })] [FontAwesomeCategoriesAttribute(new[] { "Editing" })] Unlink = 0xF127, /// <summary> /// The Font Awesome "unlock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "admin", "lock", "open", "padlock", "password", "privacy", "private", "protect", "unlock", "unlocked" })] + [FontAwesomeSearchTerms(new[] { "admin", "lock", "open", "password", "private", "protect", "unlock", "unlocked" })] [FontAwesomeCategoriesAttribute(new[] { "Security" })] Unlock = 0xF09C, /// <summary> /// The Font Awesome "unlock-keyhole" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "unlock keyhole", "admin", "lock", "padlock", "password", "privacy", "private", "protect" })] + [FontAwesomeSearchTerms(new[] { "unlock keyhole", "admin", "lock", "password", "private", "protect" })] [FontAwesomeCategoriesAttribute(new[] { "Security" })] UnlockAlt = 0xF13E, /// <summary> /// The Font Awesome "upload" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "upload", "hard drive", "import", "publish", "upgrade" })] + [FontAwesomeSearchTerms(new[] { "upload", "hard drive", "import", "publish" })] [FontAwesomeCategoriesAttribute(new[] { "Arrows", "Devices + Hardware" })] Upload = 0xF093, /// <summary> /// The Font Awesome "user" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user", "adult", "bust", "bust in silhouette", "default", "employee", "gender-neutral", "person", "profile", "silhouette", "uer", "unspecified gender", "username", "users-people" })] + [FontAwesomeSearchTerms(new[] { "user", "adult", "bust", "bust in silhouette", "gender-neutral", "person", "profile", "silhouette", "unspecified gender", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Social", "Users + People" })] User = 0xF007, /// <summary> - /// The Font Awesome "user" icon unicode character. - /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0xF007. + /// The Font Awesome "user-large" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user", "adult", "bust", "bust in silhouette", "default", "employee", "gender-neutral", "person", "profile", "silhouette", "uer", "unspecified gender", "username", "users-people" })] - [FontAwesomeCategoriesAttribute(new[] { "Social", "Users + People" })] + [FontAwesomeSearchTerms(new[] { "user large", "users-people" })] + [FontAwesomeCategoriesAttribute(new[] { "Users + People" })] UserAlt = 0xF406, /// <summary> - /// The Font Awesome "user-slash" icon unicode character. - /// Uses a legacy unicode value for backwards compatability. The current unicode value is 0xF506. + /// The Font Awesome "user-large-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user slash", "ban", "delete", "deny", "disabled", "disconnect", "employee", "remove", "uer" })] + [FontAwesomeSearchTerms(new[] { "user large slash", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Users + People" })] UserAltSlash = 0xF4FA, /// <summary> /// The Font Awesome "user-astronaut" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user astronaut", "avatar", "clothing", "cosmonaut", "nasa", "space", "suit", "uer" })] + [FontAwesomeSearchTerms(new[] { "user astronaut", "avatar", "clothing", "cosmonaut", "nasa", "space", "suit" })] [FontAwesomeCategoriesAttribute(new[] { "Astronomy", "Science Fiction", "Users + People" })] UserAstronaut = 0xF4FB, /// <summary> /// The Font Awesome "user-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user check", "employee", "enable", "uer", "users-people", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "user check", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Users + People" })] UserCheck = 0xF4FC, /// <summary> /// The Font Awesome "circle-user" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "circle user", "employee", "uer", "username", "users-people" })] + [FontAwesomeSearchTerms(new[] { "circle user", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Social", "Users + People" })] UserCircle = 0xF2BD, /// <summary> /// The Font Awesome "user-clock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user clock", "employee", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "user clock", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Users + People" })] UserClock = 0xF4FD, /// <summary> /// The Font Awesome "user-gear" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user gear", "employee", "together", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "user gear", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Users + People" })] UserCog = 0xF4FE, /// <summary> /// The Font Awesome "user-pen" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user pen", "employee", "modify", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "user pen", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Users + People" })] UserEdit = 0xF4FF, /// <summary> /// The Font Awesome "user-group" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user group", "bust", "busts in silhouette", "crowd", "employee", "silhouette", "together", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "user group", "bust", "busts in silhouette", "silhouette", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Social", "Users + People" })] UserFriends = 0xF500, /// <summary> /// The Font Awesome "user-graduate" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user graduate", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "user graduate", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Education", "Users + People" })] UserGraduate = 0xF501, /// <summary> /// The Font Awesome "user-injured" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user injured", "employee", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "user injured", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] UserInjured = 0xF728, /// <summary> /// The Font Awesome "user-lock" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user lock", "employee", "padlock", "privacy", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "user lock", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Security", "Users + People" })] UserLock = 0xF502, /// <summary> /// The Font Awesome "user-doctor" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user doctor", "covid-19", "health", "job", "medical", "nurse", "occupation", "physician", "profile", "surgeon", "uer", "worker" })] + [FontAwesomeSearchTerms(new[] { "user doctor", "covid-19", "health", "job", "medical", "nurse", "occupation", "physician", "profile", "surgeon", "worker" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Medical + Health", "Users + People" })] UserMd = 0xF0F0, /// <summary> /// The Font Awesome "user-minus" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user minus", "delete", "employee", "negative", "remove", "uer" })] + [FontAwesomeSearchTerms(new[] { "user minus", "delete", "negative", "remove" })] [FontAwesomeCategoriesAttribute(new[] { "Users + People" })] UserMinus = 0xF503, /// <summary> /// The Font Awesome "user-ninja" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user ninja", "assassin", "avatar", "dangerous", "deadly", "fighter", "hidden", "ninja", "sneaky", "stealth", "uer" })] + [FontAwesomeSearchTerms(new[] { "user ninja", "assassin", "avatar", "dangerous", "deadly", "fighter", "hidden", "ninja", "sneaky", "stealth" })] [FontAwesomeCategoriesAttribute(new[] { "Users + People" })] UserNinja = 0xF504, /// <summary> /// The Font Awesome "user-nurse" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user nurse", "covid-19", "doctor", "health", "md", "medical", "midwife", "physician", "practitioner", "surgeon", "uer", "worker" })] + [FontAwesomeSearchTerms(new[] { "user nurse", "covid-19", "doctor", "health", "md", "medical", "midwife", "physician", "practitioner", "surgeon", "worker" })] [FontAwesomeCategoriesAttribute(new[] { "Medical + Health", "Users + People" })] UserNurse = 0xF82F, /// <summary> /// The Font Awesome "user-plus" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user plus", "add", "avatar", "employee", "follow", "positive", "sign up", "signup", "team", "user" })] + [FontAwesomeSearchTerms(new[] { "user plus", "add", "avatar", "positive", "sign up", "signup", "team" })] [FontAwesomeCategoriesAttribute(new[] { "Social", "Users + People" })] UserPlus = 0xF234, /// <summary> /// The Font Awesome "users" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "users", "employee", "together", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "users", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Social", "Users + People" })] Users = 0xF0C0, /// <summary> /// The Font Awesome "users-between-lines" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "users between lines", "covered", "crowd", "employee", "group", "people", "together", "uer" })] + [FontAwesomeSearchTerms(new[] { "users between lines", "covered", "group", "people" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] UsersBetweenLines = 0xE591, /// <summary> /// The Font Awesome "users-gear" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "users gear", "employee", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "users gear", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Users + People" })] UsersCog = 0xF509, /// <summary> /// The Font Awesome "user-secret" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user secret", "detective", "sleuth", "spy", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "user secret", "detective", "sleuth", "spy", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Coding", "Security", "Users + People" })] UserSecret = 0xF21B, /// <summary> /// The Font Awesome "user-shield" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user shield", "employee", "protect", "safety", "security", "uer" })] + [FontAwesomeSearchTerms(new[] { "user shield", "protect", "safety" })] [FontAwesomeCategoriesAttribute(new[] { "Security", "Users + People" })] UserShield = 0xF505, /// <summary> /// The Font Awesome "user-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user slash", "ban", "delete", "deny", "disabled", "disconnect", "employee", "remove", "uer" })] + [FontAwesomeSearchTerms(new[] { "user slash", "ban", "delete", "remove" })] [FontAwesomeCategoriesAttribute(new[] { "Users + People" })] UserSlash = 0xF506, /// <summary> /// The Font Awesome "users-line" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "users line", "crowd", "employee", "group", "need", "people", "together", "uer" })] + [FontAwesomeSearchTerms(new[] { "users line", "group", "need", "people" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] UsersLine = 0xE592, /// <summary> /// The Font Awesome "users-rays" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "users rays", "affected", "crowd", "employee", "focused", "group", "people", "uer" })] + [FontAwesomeSearchTerms(new[] { "users rays", "affected", "focused", "group", "people" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] UsersRays = 0xE593, /// <summary> /// The Font Awesome "users-rectangle" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "users rectangle", "crowd", "employee", "focus", "group", "people", "reached", "uer" })] + [FontAwesomeSearchTerms(new[] { "users rectangle", "focus", "group", "people", "reached" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] UsersRectangle = 0xE594, /// <summary> /// The Font Awesome "users-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "users slash", "disabled", "disconnect", "employee", "together", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "users slash", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Users + People" })] UsersSlash = 0xE073, /// <summary> /// The Font Awesome "users-viewfinder" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "users viewfinder", "crowd", "focus", "group", "people", "targeted", "uer" })] + [FontAwesomeSearchTerms(new[] { "users viewfinder", "focus", "group", "people", "targeted" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Users + People" })] UsersViewfinder = 0xE595, /// <summary> /// The Font Awesome "user-tag" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user tag", "employee", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "user tag", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Users + People" })] UserTag = 0xF507, /// <summary> /// The Font Awesome "user-tie" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user tie", "administrator", "avatar", "business", "clothing", "employee", "formal", "offer", "portfolio", "professional", "suit", "uer" })] + [FontAwesomeSearchTerms(new[] { "user tie", "avatar", "business", "clothing", "formal", "professional", "suit" })] [FontAwesomeCategoriesAttribute(new[] { "Clothing + Fashion", "Users + People" })] UserTie = 0xF508, /// <summary> /// The Font Awesome "user-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "user xmark", "archive", "delete", "employee", "remove", "uer", "uncheck", "x" })] + [FontAwesomeSearchTerms(new[] { "user xmark", "archive", "delete", "remove", "x" })] [FontAwesomeCategoriesAttribute(new[] { "Users + People" })] UserTimes = 0xF235, @@ -9299,14 +9134,15 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "vault" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "vault", "bank", "important", "investment", "lock", "money", "premium", "privacy", "safe", "salary" })] + [FontAwesomeSearchTerms(new[] { "vault", "bank", "important", "lock", "money", "safe" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Money", "Security" })] Vault = 0xE2C5, /// <summary> - /// The Font Awesome "vectorsquare" icon unicode character. + /// The Font Awesome "vector-square" icon unicode character. /// </summary> - [Obsolete] + [FontAwesomeSearchTerms(new[] { "vector square", "anchors", "lines", "object", "render", "shape" })] + [FontAwesomeCategoriesAttribute(new[] { "Design" })] VectorSquare = 0xF5CB, /// <summary> @@ -9347,21 +9183,21 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "vial" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "vial", "ampule", "chemist", "chemistry", "experiment", "knowledge", "lab", "sample", "science", "test", "test tube" })] + [FontAwesomeSearchTerms(new[] { "vial", "ampule", "chemist", "chemistry", "experiment", "lab", "sample", "science", "test", "test tube" })] [FontAwesomeCategoriesAttribute(new[] { "Medical + Health", "Science" })] Vial = 0xF492, /// <summary> /// The Font Awesome "vial-circle-check" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "vial circle check", "ampule", "chemist", "chemistry", "enable", "not affected", "ok", "okay", "success", "test tube", "tube", "vaccine", "validate", "working" })] + [FontAwesomeSearchTerms(new[] { "vial circle check", "ampule", "chemist", "chemistry", "not affected", "ok", "okay", "success", "test tube", "tube", "vaccine" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Medical + Health", "Science" })] VialCircleCheck = 0xE596, /// <summary> /// The Font Awesome "vials" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "vials", "ampule", "experiment", "knowledge", "lab", "sample", "science", "test", "test tube" })] + [FontAwesomeSearchTerms(new[] { "vials", "ampule", "experiment", "lab", "sample", "science", "test", "test tube" })] [FontAwesomeCategoriesAttribute(new[] { "Medical + Health", "Science" })] Vials = 0xF493, @@ -9382,7 +9218,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "video-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "video slash", "add", "create", "disabled", "disconnect", "film", "new", "positive", "record", "video" })] + [FontAwesomeSearchTerms(new[] { "video slash", "add", "create", "film", "new", "positive", "record", "video" })] [FontAwesomeCategoriesAttribute(new[] { "Communication", "Film + Video" })] VideoSlash = 0xF4E2, @@ -9410,7 +9246,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "virus-covid-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "virus covid slash", "bug", "covid-19", "disabled", "flu", "health", "infection", "pandemic", "vaccine", "viral", "virus" })] + [FontAwesomeSearchTerms(new[] { "virus covid slash", "bug", "covid-19", "flu", "health", "infection", "pandemic", "vaccine", "viral", "virus" })] [FontAwesomeCategoriesAttribute(new[] { "Medical + Health" })] VirusCovidSlash = 0xE4A9, @@ -9424,7 +9260,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "virus-slash" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "virus slash", "bug", "coronavirus", "covid-19", "cure", "disabled", "eliminate", "flu", "health", "infection", "pandemic", "sick", "vaccine", "viral" })] + [FontAwesomeSearchTerms(new[] { "virus slash", "bug", "coronavirus", "covid-19", "cure", "eliminate", "flu", "health", "infection", "pandemic", "sick", "vaccine", "viral" })] [FontAwesomeCategoriesAttribute(new[] { "Medical + Health" })] VirusSlash = 0xE075, @@ -9480,7 +9316,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "check-to-slot" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "check to slot", "accept", "cast", "election", "enable", "politics", "positive", "validate", "voting", "working", "yes" })] + [FontAwesomeSearchTerms(new[] { "check to slot", "accept", "cast", "election", "politics", "positive", "voting", "yes" })] [FontAwesomeCategoriesAttribute(new[] { "Political" })] VoteYea = 0xF772, @@ -9501,14 +9337,14 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "person-walking" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "person walking", "crosswalk", "exercise", "follow", "hike", "move", "person walking", "uer", "walk", "walking", "workout" })] + [FontAwesomeSearchTerms(new[] { "person walking", "crosswalk", "exercise", "hike", "move", "person walking", "walk", "walking" })] [FontAwesomeCategoriesAttribute(new[] { "Humanitarian", "Sports + Fitness", "Users + People" })] Walking = 0xF554, /// <summary> /// The Font Awesome "wallet" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "wallet", "billfold", "cash", "currency", "money", "salary" })] + [FontAwesomeSearchTerms(new[] { "wallet", "billfold", "cash", "currency", "money" })] [FontAwesomeCategoriesAttribute(new[] { "Business", "Money" })] Wallet = 0xF555, @@ -9544,16 +9380,9 @@ public enum FontAwesomeIcon /// The Font Awesome "wave-square" icon unicode character. /// </summary> [FontAwesomeSearchTerms(new[] { "wave square", "frequency", "pulse", "signal" })] - [FontAwesomeCategoriesAttribute(new[] { "Mathematics", "Music + Audio" })] + [FontAwesomeCategoriesAttribute(new[] { "Mathematics" })] WaveSquare = 0xF83E, - /// <summary> - /// The Font Awesome "web-awesome" icon unicode character. - /// </summary> - [FontAwesomeSearchTerms(new[] { "web awesome", "awesome", "coding", "components", "crown", "web" })] - [FontAwesomeCategoriesAttribute(new[] { "Coding", "Design" })] - WebAwesome = 0xE682, - /// <summary> /// The Font Awesome "weight-scale" icon unicode character. /// </summary> @@ -9578,28 +9407,28 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "wheat-awn-circle-exclamation" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "wheat awn circle exclamation", "affected", "failed", "famine", "food", "gluten", "hunger", "starve", "straw" })] + [FontAwesomeSearchTerms(new[] { "wheat awn circle exclamation", "affected", "famine", "food", "gluten", "hunger", "starve", "straw" })] [FontAwesomeCategoriesAttribute(new[] { "Disaster + Crisis", "Food + Beverage", "Humanitarian" })] WheatAwnCircleExclamation = 0xE598, /// <summary> /// The Font Awesome "wheelchair" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "wheelchair", "disabled", "uer", "users-people" })] + [FontAwesomeSearchTerms(new[] { "wheelchair", "users-people" })] [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Maps", "Medical + Health", "Transportation", "Travel + Hotel", "Users + People" })] Wheelchair = 0xF193, /// <summary> /// The Font Awesome "wheelchair-move" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "wheelchair move", "access", "disabled", "handicap", "impairment", "physical", "uer", "wheelchair symbol" })] + [FontAwesomeSearchTerms(new[] { "wheelchair move", "access", "handicap", "impairment", "physical", "wheelchair symbol" })] [FontAwesomeCategoriesAttribute(new[] { "Accessibility", "Humanitarian", "Maps", "Medical + Health", "Transportation", "Travel + Hotel", "Users + People" })] WheelchairMove = 0xE2CE, /// <summary> /// The Font Awesome "wifi" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "wifi", "connection", "hotspot", "internet", "network", "signal", "wireless", "www" })] + [FontAwesomeSearchTerms(new[] { "wifi", "connection", "hotspot", "internet", "network", "wireless" })] [FontAwesomeCategoriesAttribute(new[] { "Connectivity", "Humanitarian", "Maps", "Toggle", "Travel + Hotel" })] Wifi = 0xF1EB, @@ -9613,7 +9442,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "rectangle-xmark" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "rectangle xmark", "browser", "cancel", "computer", "development", "uncheck" })] + [FontAwesomeSearchTerms(new[] { "rectangle xmark", "browser", "cancel", "computer", "development" })] [FontAwesomeCategoriesAttribute(new[] { "Coding" })] WindowClose = 0xF410, @@ -9676,7 +9505,7 @@ public enum FontAwesomeIcon /// <summary> /// The Font Awesome "wrench" icon unicode character. /// </summary> - [FontAwesomeSearchTerms(new[] { "configuration", "construction", "equipment", "fix", "mechanic", "modify", "plumbing", "settings", "spanner", "tool", "update", "wrench" })] + [FontAwesomeSearchTerms(new[] { "construction", "fix", "mechanic", "plumbing", "settings", "spanner", "tool", "update", "wrench" })] [FontAwesomeCategoriesAttribute(new[] { "Construction", "Maps" })] Wrench = 0xF0AD, diff --git a/Dalamud/Interface/FontIdentifier/DalamudAssetFontAndFamilyId.cs b/Dalamud/Interface/FontIdentifier/DalamudAssetFontAndFamilyId.cs index 3778ea0de..c531dced5 100644 --- a/Dalamud/Interface/FontIdentifier/DalamudAssetFontAndFamilyId.cs +++ b/Dalamud/Interface/FontIdentifier/DalamudAssetFontAndFamilyId.cs @@ -3,9 +3,7 @@ using System.Collections.Generic; using Dalamud.Bindings.ImGui; using Dalamud.Interface.ManagedFontAtlas; using Dalamud.Storage.Assets; - using Newtonsoft.Json; - using TerraFX.Interop.DirectX; namespace Dalamud.Interface.FontIdentifier; diff --git a/Dalamud/Interface/FontIdentifier/DalamudDefaultFontAndFamilyId.cs b/Dalamud/Interface/FontIdentifier/DalamudDefaultFontAndFamilyId.cs index 4666de54a..c45cf256b 100644 --- a/Dalamud/Interface/FontIdentifier/DalamudDefaultFontAndFamilyId.cs +++ b/Dalamud/Interface/FontIdentifier/DalamudDefaultFontAndFamilyId.cs @@ -2,9 +2,7 @@ using System.Collections.Generic; using Dalamud.Bindings.ImGui; using Dalamud.Interface.ManagedFontAtlas; - using Newtonsoft.Json; - using TerraFX.Interop.DirectX; namespace Dalamud.Interface.FontIdentifier; diff --git a/Dalamud/Interface/FontIdentifier/GameFontAndFamilyId.cs b/Dalamud/Interface/FontIdentifier/GameFontAndFamilyId.cs index f19a2ec6a..e294c8813 100644 --- a/Dalamud/Interface/FontIdentifier/GameFontAndFamilyId.cs +++ b/Dalamud/Interface/FontIdentifier/GameFontAndFamilyId.cs @@ -3,9 +3,7 @@ using System.Collections.Generic; using Dalamud.Bindings.ImGui; using Dalamud.Interface.GameFonts; using Dalamud.Interface.ManagedFontAtlas; - using Newtonsoft.Json; - using TerraFX.Interop.DirectX; namespace Dalamud.Interface.FontIdentifier; diff --git a/Dalamud/Interface/FontIdentifier/IFontFamilyId.cs b/Dalamud/Interface/FontIdentifier/IFontFamilyId.cs index 40780422a..991716f74 100644 --- a/Dalamud/Interface/FontIdentifier/IFontFamilyId.cs +++ b/Dalamud/Interface/FontIdentifier/IFontFamilyId.cs @@ -36,25 +36,26 @@ public interface IFontFamilyId : IObjectWithLocalizableName /// </summary> /// <returns>The list of fonts.</returns> public static List<IFontFamilyId> ListDalamudFonts() => - [ + new() + { new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansJpMedium), new DalamudAssetFontAndFamilyId(DalamudAsset.InconsolataRegular), new DalamudAssetFontAndFamilyId(DalamudAsset.FontAwesomeFreeSolid), - ]; + }; /// <summary> /// Gets the list of Game-provided fonts. /// </summary> /// <returns>The list of fonts.</returns> - public static List<IFontFamilyId> ListGameFonts() => - [ + public static List<IFontFamilyId> ListGameFonts() => new() + { new GameFontAndFamilyId(GameFontFamily.Axis), new GameFontAndFamilyId(GameFontFamily.Jupiter), new GameFontAndFamilyId(GameFontFamily.JupiterNumeric), new GameFontAndFamilyId(GameFontFamily.Meidinger), new GameFontAndFamilyId(GameFontFamily.MiedingerMid), new GameFontAndFamilyId(GameFontFamily.TrumpGothic), - ]; + }; /// <summary> /// Gets the list of System-provided fonts. diff --git a/Dalamud/Interface/FontIdentifier/IObjectWithLocalizableName.cs b/Dalamud/Interface/FontIdentifier/IObjectWithLocalizableName.cs index 4b3860431..2b970a5fd 100644 --- a/Dalamud/Interface/FontIdentifier/IObjectWithLocalizableName.cs +++ b/Dalamud/Interface/FontIdentifier/IObjectWithLocalizableName.cs @@ -64,9 +64,9 @@ public interface IObjectWithLocalizableName var result = new Dictionary<string, string>((int)count); for (var i = 0u; i < count; i++) { - fn->GetLocaleName(i, buf, maxStrLen).ThrowOnError(); + fn->GetLocaleName(i, (ushort*)buf, maxStrLen).ThrowOnError(); var key = new string(buf); - fn->GetString(i, buf, maxStrLen).ThrowOnError(); + fn->GetString(i, (ushort*)buf, maxStrLen).ThrowOnError(); var value = new string(buf); result[key.ToLowerInvariant()] = value; } diff --git a/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs b/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs index b1c03f9dd..070b1c1e1 100644 --- a/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs +++ b/Dalamud/Interface/FontIdentifier/SingleFontSpec.cs @@ -6,7 +6,6 @@ using System.Text; using Dalamud.Bindings.ImGui; using Dalamud.Interface.ManagedFontAtlas; using Dalamud.Interface.Utility; - using Newtonsoft.Json; namespace Dalamud.Interface.FontIdentifier; diff --git a/Dalamud/Interface/FontIdentifier/SystemFontFamilyId.cs b/Dalamud/Interface/FontIdentifier/SystemFontFamilyId.cs index 60e20fb6f..420ee77a4 100644 --- a/Dalamud/Interface/FontIdentifier/SystemFontFamilyId.cs +++ b/Dalamud/Interface/FontIdentifier/SystemFontFamilyId.cs @@ -83,7 +83,7 @@ public sealed class SystemFontFamilyId : IFontFamilyId else if (candidates.Any(x => x.Style == (int)DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL)) candidates.RemoveAll(x => x.Style != (int)DWRITE_FONT_STYLE.DWRITE_FONT_STYLE_NORMAL); - if (candidates.Count == 0) + if (!candidates.Any()) return 0; for (var i = 0; i < this.Fonts.Count; i++) @@ -117,7 +117,7 @@ public sealed class SystemFontFamilyId : IFontFamilyId return new(IObjectWithLocalizableName.GetLocaleNames(fn)); } - private unsafe List<IFontId> GetFonts() + private unsafe IReadOnlyList<IFontId> GetFonts() { using var dwf = default(ComPtr<IDWriteFactory>); fixed (Guid* piid = &IID.IID_IDWriteFactory) @@ -133,8 +133,8 @@ public sealed class SystemFontFamilyId : IFontFamilyId var familyIndex = 0u; BOOL exists = false; - fixed (char* pName = this.EnglishName) - sfc.Get()->FindFamilyName(pName, &familyIndex, &exists).ThrowOnError(); + fixed (void* pName = this.EnglishName) + sfc.Get()->FindFamilyName((ushort*)pName, &familyIndex, &exists).ThrowOnError(); if (!exists) throw new FileNotFoundException($"Font \"{this.EnglishName}\" not found."); diff --git a/Dalamud/Interface/FontIdentifier/SystemFontId.cs b/Dalamud/Interface/FontIdentifier/SystemFontId.cs index 45885a9a8..e11759a88 100644 --- a/Dalamud/Interface/FontIdentifier/SystemFontId.cs +++ b/Dalamud/Interface/FontIdentifier/SystemFontId.cs @@ -5,9 +5,7 @@ using System.Linq; using Dalamud.Bindings.ImGui; using Dalamud.Interface.ManagedFontAtlas; using Dalamud.Utility; - using Newtonsoft.Json; - using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; @@ -115,8 +113,8 @@ public sealed class SystemFontId : IFontId var familyIndex = 0u; BOOL exists = false; - fixed (char* name = this.Family.EnglishName) - sfc.Get()->FindFamilyName(name, &familyIndex, &exists).ThrowOnError(); + fixed (void* name = this.Family.EnglishName) + sfc.Get()->FindFamilyName((ushort*)name, &familyIndex, &exists).ThrowOnError(); if (!exists) throw new FileNotFoundException($"Font \"{this.Family.EnglishName}\" not found."); @@ -153,7 +151,7 @@ public sealed class SystemFontId : IFontId flocal.Get()->GetFilePathLengthFromKey(refKey, refKeySize, &pathSize).ThrowOnError(); var path = stackalloc char[(int)pathSize + 1]; - flocal.Get()->GetFilePathFromKey(refKey, refKeySize, path, pathSize + 1).ThrowOnError(); + flocal.Get()->GetFilePathFromKey(refKey, refKeySize, (ushort*)path, pathSize + 1).ThrowOnError(); return (new(path, 0, (int)pathSize), (int)fface.Get()->GetIndex()); } diff --git a/Dalamud/Interface/GameFonts/FdtReader.cs b/Dalamud/Interface/GameFonts/FdtReader.cs index 6f6aaea25..0e8f3fb59 100644 --- a/Dalamud/Interface/GameFonts/FdtReader.cs +++ b/Dalamud/Interface/GameFonts/FdtReader.cs @@ -43,12 +43,12 @@ public class FdtReader /// <summary> /// Gets all the glyphs defined in this file. /// </summary> - public List<FontTableEntry> Glyphs { get; init; } = []; + public List<FontTableEntry> Glyphs { get; init; } = new(); /// <summary> /// Gets all the kerning entries defined in this file. /// </summary> - public List<KerningTableEntry> Distances { get; init; } = []; + public List<KerningTableEntry> Distances { get; init; } = new(); /// <summary> /// Finds the glyph index for the corresponding codepoint. @@ -269,7 +269,7 @@ public class FdtReader /// <summary> /// Mapping of texture channel index to byte index. /// </summary> - public static readonly int[] TextureChannelOrder = [2, 1, 0, 3]; + public static readonly int[] TextureChannelOrder = { 2, 1, 0, 3 }; /// <summary> /// Integer representation of a Unicode character in UTF-8 in reverse order, read in little endian. diff --git a/Dalamud/Interface/GameFonts/GameFontLayoutPlan.cs b/Dalamud/Interface/GameFonts/GameFontLayoutPlan.cs index 126e7601f..5b0fe100b 100644 --- a/Dalamud/Interface/GameFonts/GameFontLayoutPlan.cs +++ b/Dalamud/Interface/GameFonts/GameFontLayoutPlan.cs @@ -200,25 +200,30 @@ public class GameFontLayoutPlan return false; // TODO: Whatever - return char.GetUnicodeCategory((char)this.Codepoint) - is System.Globalization.UnicodeCategory.SpaceSeparator - or System.Globalization.UnicodeCategory.LineSeparator - or System.Globalization.UnicodeCategory.ParagraphSeparator - or System.Globalization.UnicodeCategory.Control - or System.Globalization.UnicodeCategory.Format - or System.Globalization.UnicodeCategory.Surrogate - or System.Globalization.UnicodeCategory.PrivateUse - or System.Globalization.UnicodeCategory.ConnectorPunctuation - or System.Globalization.UnicodeCategory.DashPunctuation - or System.Globalization.UnicodeCategory.OpenPunctuation - or System.Globalization.UnicodeCategory.ClosePunctuation - or System.Globalization.UnicodeCategory.InitialQuotePunctuation - or System.Globalization.UnicodeCategory.FinalQuotePunctuation - or System.Globalization.UnicodeCategory.OtherPunctuation - or System.Globalization.UnicodeCategory.MathSymbol - or System.Globalization.UnicodeCategory.ModifierSymbol - or System.Globalization.UnicodeCategory.OtherSymbol - or System.Globalization.UnicodeCategory.OtherNotAssigned; + switch (char.GetUnicodeCategory((char)this.Codepoint)) + { + case System.Globalization.UnicodeCategory.SpaceSeparator: + case System.Globalization.UnicodeCategory.LineSeparator: + case System.Globalization.UnicodeCategory.ParagraphSeparator: + case System.Globalization.UnicodeCategory.Control: + case System.Globalization.UnicodeCategory.Format: + case System.Globalization.UnicodeCategory.Surrogate: + case System.Globalization.UnicodeCategory.PrivateUse: + case System.Globalization.UnicodeCategory.ConnectorPunctuation: + case System.Globalization.UnicodeCategory.DashPunctuation: + case System.Globalization.UnicodeCategory.OpenPunctuation: + case System.Globalization.UnicodeCategory.ClosePunctuation: + case System.Globalization.UnicodeCategory.InitialQuotePunctuation: + case System.Globalization.UnicodeCategory.FinalQuotePunctuation: + case System.Globalization.UnicodeCategory.OtherPunctuation: + case System.Globalization.UnicodeCategory.MathSymbol: + case System.Globalization.UnicodeCategory.ModifierSymbol: + case System.Globalization.UnicodeCategory.OtherSymbol: + case System.Globalization.UnicodeCategory.OtherNotAssigned: + return true; + } + + return false; } } } @@ -295,7 +300,7 @@ public class GameFontLayoutPlan elements.Add(new() { Codepoint = c, Glyph = this.fdt.GetGlyph(c), }); var lastBreakIndex = 0; - List<int> lineBreakIndices = [0]; + List<int> lineBreakIndices = new() { 0 }; for (var i = 1; i < elements.Count; i++) { var prev = elements[i - 1]; diff --git a/Dalamud/Interface/GlyphRangesJapanese.cs b/Dalamud/Interface/GlyphRangesJapanese.cs index 2acf7f40e..2773b9db5 100644 --- a/Dalamud/Interface/GlyphRangesJapanese.cs +++ b/Dalamud/Interface/GlyphRangesJapanese.cs @@ -8,8 +8,8 @@ public static class GlyphRangesJapanese /// <summary> /// Gets the unicode glyph ranges for the Japanese language. /// </summary> - public static ushort[] GlyphRanges => - [ + public static ushort[] GlyphRanges => new ushort[] + { 0x0020, 0x00FF, 0x0391, 0x03A1, 0x03A3, 0x03A9, 0x03B1, 0x03C1, 0x03C3, 0x03C9, 0x0401, 0x0401, 0x0410, 0x044F, 0x0451, 0x0451, 0x2000, 0x206F, 0x2103, 0x2103, 0x212B, 0x212B, 0x2190, 0x2193, 0x21D2, 0x21D2, 0x21D4, 0x21D4, 0x2200, 0x2200, 0x2202, 0x2203, 0x2207, 0x2208, 0x220B, 0x220B, 0x2212, 0x2212, 0x221A, 0x221A, 0x221D, 0x221E, 0x2220, 0x2220, 0x2227, 0x222C, 0x2234, 0x2235, @@ -524,5 +524,5 @@ public static class GlyphRangesJapanese 0x9F4E, 0x9F4F, 0x9F52, 0x9F52, 0x9F54, 0x9F54, 0x9F5F, 0x9F63, 0x9F66, 0x9F67, 0x9F6A, 0x9F6A, 0x9F6C, 0x9F6C, 0x9F72, 0x9F72, 0x9F76, 0x9F77, 0x9F8D, 0x9F8D, 0x9F95, 0x9F95, 0x9F9C, 0x9F9D, 0x9FA0, 0x9FA0, 0xFF01, 0xFF01, 0xFF03, 0xFF06, 0xFF08, 0xFF0C, 0xFF0E, 0xFF3B, 0xFF3D, 0xFF5D, 0xFF61, 0xFF9F, 0xFFE3, 0xFFE3, 0xFFE5, 0xFFE5, 0xFFFF, 0xFFFF, 0, - ]; + }; } diff --git a/Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs b/Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs index e3b98ec37..ea609828d 100644 --- a/Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs +++ b/Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs @@ -1,4 +1,9 @@ +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGuizmo; @@ -9,6 +14,8 @@ using Dalamud.Interface.ImGuiBackend.InputHandler; using Dalamud.Interface.ImGuiBackend.Renderers; using Dalamud.Utility; +using Serilog; + using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; diff --git a/Dalamud/Interface/ImGuiBackend/Helpers/ReShadePeeler.cs b/Dalamud/Interface/ImGuiBackend/Helpers/ReShadePeeler.cs index 3f3c98c26..824ba382a 100644 --- a/Dalamud/Interface/ImGuiBackend/Helpers/ReShadePeeler.cs +++ b/Dalamud/Interface/ImGuiBackend/Helpers/ReShadePeeler.cs @@ -104,19 +104,19 @@ internal static unsafe class ReShadePeeler fixed (byte* pfn5 = "glBegin"u8) fixed (byte* pfn6 = "vkCreateDevice"u8) { - if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn0) == null) + if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn0) == 0) continue; - if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn1) == null) + if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn1) == 0) continue; - if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn2) == null) + if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn2) == 0) continue; - if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn3) == null) + if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn3) == 0) continue; - if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn4) == null) + if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn4) == 0) continue; - if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn5) == null) + if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn5) == 0) continue; - if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn6) == null) + if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn6) == 0) continue; } diff --git a/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.StaticLookupFunctions.cs b/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.StaticLookupFunctions.cs index a7b70ce35..5710a5991 100644 --- a/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.StaticLookupFunctions.cs +++ b/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.StaticLookupFunctions.cs @@ -299,12 +299,11 @@ internal sealed partial class Win32InputHandler private static void ViewportFlagsToWin32Styles(ImGuiViewportFlags flags, out int style, out int exStyle) { - style = (flags & ImGuiViewportFlags.NoDecoration) != 0 ? unchecked((int)WS.WS_POPUP) : WS.WS_OVERLAPPEDWINDOW; - exStyle = (flags & ImGuiViewportFlags.NoTaskBarIcon) != 0 ? WS.WS_EX_TOOLWINDOW : WS.WS_EX_APPWINDOW; + style = (int)(flags.HasFlag(ImGuiViewportFlags.NoDecoration) ? WS.WS_POPUP : WS.WS_OVERLAPPEDWINDOW); + exStyle = + (int)(flags.HasFlag(ImGuiViewportFlags.NoTaskBarIcon) ? WS.WS_EX_TOOLWINDOW : (uint)WS.WS_EX_APPWINDOW); exStyle |= WS.WS_EX_NOREDIRECTIONBITMAP; - if ((flags & ImGuiViewportFlags.TopMost) != 0) + if (flags.HasFlag(ImGuiViewportFlags.TopMost)) exStyle |= WS.WS_EX_TOPMOST; - if ((flags & ImGuiViewportFlags.NoInputs) != 0) - exStyle |= WS.WS_EX_TRANSPARENT | WS.WS_EX_LAYERED; } } diff --git a/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs b/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs index ea60be98d..596df4c67 100644 --- a/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs +++ b/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs @@ -7,9 +7,7 @@ using System.Runtime.InteropServices; using System.Text; using Dalamud.Bindings.ImGui; -using Dalamud.Console; using Dalamud.Memory; -using Dalamud.Utility; using Serilog; @@ -36,14 +34,11 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler private readonly HCURSOR[] cursors; private readonly WndProcDelegate wndProcDelegate; + private readonly bool[] imguiMouseIsDown; private readonly nint platformNamePtr; - private readonly IConsoleVariable<bool> cvLogMouseEvents; - private ViewportHandler viewportHandler; - private int mouseButtonsDown; - private bool mouseTracked; private long lastTime; private nint iniPathPtr; @@ -69,8 +64,7 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors | ImGuiBackendFlags.HasSetMousePos | ImGuiBackendFlags.RendererHasViewports | - ImGuiBackendFlags.PlatformHasViewports | - ImGuiBackendFlags.HasMouseHoveredViewport; + ImGuiBackendFlags.PlatformHasViewports; this.platformNamePtr = Marshal.StringToHGlobalAnsi("imgui_impl_win32_c#"); io.Handle->BackendPlatformName = (byte*)this.platformNamePtr; @@ -80,6 +74,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler if (io.ConfigFlags.HasFlag(ImGuiConfigFlags.ViewportsEnable)) this.viewportHandler = new(this); + this.imguiMouseIsDown = new bool[5]; + this.cursors = new HCURSOR[9]; this.cursors[(int)ImGuiMouseCursor.Arrow] = LoadCursorW(default, IDC.IDC_ARROW); this.cursors[(int)ImGuiMouseCursor.TextInput] = LoadCursorW(default, IDC.IDC_IBEAM); @@ -90,11 +86,6 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler this.cursors[(int)ImGuiMouseCursor.ResizeNwse] = LoadCursorW(default, IDC.IDC_SIZENWSE); this.cursors[(int)ImGuiMouseCursor.Hand] = LoadCursorW(default, IDC.IDC_HAND); this.cursors[(int)ImGuiMouseCursor.NotAllowed] = LoadCursorW(default, IDC.IDC_NO); - - this.cvLogMouseEvents = Service<ConsoleManager>.Get().AddVariable( - "imgui.log_mouse_events", - "Log mouse events to console for debugging", - false); } /// <summary> @@ -104,6 +95,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler private delegate LRESULT WndProcDelegate(HWND hWnd, uint uMsg, WPARAM wparam, LPARAM lparam); + private delegate BOOL MonitorEnumProcDelegate(HMONITOR monitor, HDC hdc, RECT* rect, LPARAM lparam); + /// <inheritdoc/> public bool UpdateCursor { get; set; } = true; @@ -162,7 +155,6 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler public void NewFrame(int targetWidth, int targetHeight) { var io = ImGui.GetIO(); - var focusedWindow = GetForegroundWindow(); io.DisplaySize.X = targetWidth; io.DisplaySize.Y = targetHeight; @@ -176,9 +168,9 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler this.viewportHandler.UpdateMonitors(); - this.UpdateMouseData(focusedWindow); + this.UpdateMousePos(); - this.ProcessKeyEventsWorkarounds(focusedWindow); + this.ProcessKeyEventsWorkarounds(); // TODO: need to figure out some way to unify all this // The bottom case works(?) if the caller hooks SetCursor, but otherwise causes fps issues @@ -232,40 +224,6 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler switch (msg) { - case WM.WM_MOUSEMOVE: - { - if (!this.mouseTracked) - { - var tme = new TRACKMOUSEEVENT - { - cbSize = (uint)sizeof(TRACKMOUSEEVENT), - dwFlags = TME.TME_LEAVE, - hwndTrack = hWndCurrent, - }; - this.mouseTracked = TrackMouseEvent(&tme); - } - - var mousePos = new POINT(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) != 0) - ClientToScreen(hWndCurrent, &mousePos); - io.AddMousePosEvent(mousePos.x, mousePos.y); - break; - } - - case WM.WM_MOUSELEAVE: - { - this.mouseTracked = false; - var mouseScreenPos = new POINT(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - ClientToScreen(hWndCurrent, &mouseScreenPos); - if (this.ViewportFromPoint(mouseScreenPos).IsNull) - { - var fltMax = ImGuiNative.GETFLTMAX(); - io.AddMousePosEvent(-fltMax, -fltMax); - } - - break; - } - case WM.WM_LBUTTONDOWN: case WM.WM_LBUTTONDBLCLK: case WM.WM_RBUTTONDOWN: @@ -275,25 +233,14 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler case WM.WM_XBUTTONDOWN: case WM.WM_XBUTTONDBLCLK: { - if (this.cvLogMouseEvents.Value) - { - Log.Verbose( - "Handle MouseDown {Btn} WantCaptureMouse: {Want} mouseButtonsDown: {Down}", - GetButton(msg, wParam), - io.WantCaptureMouse, - this.mouseButtonsDown); - } - var button = GetButton(msg, wParam); if (io.WantCaptureMouse) { - if (this.mouseButtonsDown == 0 && GetCapture() == nint.Zero) - { + if (!ImGui.IsAnyMouseDown() && GetCapture() == nint.Zero) SetCapture(hWndCurrent); - } - this.mouseButtonsDown |= 1 << button; - io.AddMouseButtonEvent(button, true); + io.MouseDown[button] = true; + this.imguiMouseIsDown[button] = true; return default(LRESULT); } @@ -308,29 +255,14 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler case WM.WM_MBUTTONUP: case WM.WM_XBUTTONUP: { - if (this.cvLogMouseEvents.Value) - { - Log.Verbose( - "Handle MouseUp {Btn} WantCaptureMouse: {Want} mouseButtonsDown: {Down}", - GetButton(msg, wParam), - io.WantCaptureMouse, - this.mouseButtonsDown); - } - var button = GetButton(msg, wParam); - - // Need to check if we captured the button event away from the game here, otherwise the game might get - // a down event but no up event, causing the cursor to get stuck. - // Can happen if WantCaptureMouse becomes true in between down and up - if (io.WantCaptureMouse && (this.mouseButtonsDown & (1 << button)) != 0) + if (io.WantCaptureMouse && this.imguiMouseIsDown[button]) { - this.mouseButtonsDown &= ~(1 << button); - if (this.mouseButtonsDown == 0 && GetCapture() == hWndCurrent) - { + if (!ImGui.IsAnyMouseDown() && GetCapture() == hWndCurrent) ReleaseCapture(); - } - io.AddMouseButtonEvent(button, false); + io.MouseDown[button] = false; + this.imguiMouseIsDown[button] = false; return default(LRESULT); } @@ -340,7 +272,7 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler case WM.WM_MOUSEWHEEL: if (io.WantCaptureMouse) { - io.AddMouseWheelEvent(0, GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA); + io.MouseWheel += GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; return default(LRESULT); } @@ -348,7 +280,7 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler case WM.WM_MOUSEHWHEEL: if (io.WantCaptureMouse) { - io.AddMouseWheelEvent(GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0); + io.MouseWheelH += GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; return default(LRESULT); } @@ -442,89 +374,66 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler this.viewportHandler.UpdateMonitors(); break; - case WM.WM_SETFOCUS when hWndCurrent == this.hWnd: - io.AddFocusEvent(true); - break; - case WM.WM_KILLFOCUS when hWndCurrent == this.hWnd: - io.AddFocusEvent(false); - // if (!ImGui.IsAnyMouseDown() && GetCapture() == hWndCurrent) - // ReleaseCapture(); - // - // ImGui.GetIO().WantCaptureMouse = false; - // ImGui.ClearWindowFocus(); + if (!ImGui.IsAnyMouseDown() && GetCapture() == hWndCurrent) + ReleaseCapture(); + + ImGui.GetIO().WantCaptureMouse = false; + ImGui.ClearWindowFocus(); break; } return null; } - private void UpdateMouseData(HWND focusedWindow) + private void UpdateMousePos() { var io = ImGui.GetIO(); + var pt = default(POINT); - var mouseScreenPos = default(POINT); - var hasMouseScreenPos = GetCursorPos(&mouseScreenPos) != 0; - - var isAppFocused = - focusedWindow != default - && (focusedWindow == this.hWnd - || IsChild(focusedWindow, this.hWnd) - || !ImGui.FindViewportByPlatformHandle(focusedWindow).IsNull); - - if (isAppFocused) + // Depending on if Viewports are enabled, we have to change how we process + // the cursor position. If viewports are enabled, we pass the absolute cursor + // position to ImGui. Otherwise, we use the old method of passing client-local + // mouse position to ImGui. + if (io.ConfigFlags.HasFlag(ImGuiConfigFlags.ViewportsEnable)) { - // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - // When multi-viewports are enabled, all Dear ImGui positions are same as OS positions. if (io.WantSetMousePos) { - var pos = new POINT((int)io.MousePos.X, (int)io.MousePos.Y); - if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) != 0) - ClientToScreen(this.hWnd, &pos); - SetCursorPos(pos.x, pos.y); + SetCursorPos((int)io.MousePos.X, (int)io.MousePos.Y); } - } - // (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured) - if (!io.WantSetMousePos && !this.mouseTracked && hasMouseScreenPos) - { - // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) - // (This is the position you can get with ::GetCursorPos() + ::ScreenToClient() or WM_MOUSEMOVE.) - // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) - // (This is the position you can get with ::GetCursorPos() or WM_MOUSEMOVE + ::ClientToScreen(). In theory adding viewport->Pos to a client position would also be the same.) - var mousePos = mouseScreenPos; - if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) == 0) + if (GetCursorPos(&pt)) { - // Use game window, otherwise, positions are calculated based on the focused window which might not be the game. - // Leads to offsets. - ClientToScreen(this.hWnd, &mousePos); + io.MousePos.X = pt.x; + io.MousePos.Y = pt.y; + } + else + { + io.MousePos.X = float.MinValue; + io.MousePos.Y = float.MinValue; } - - io.AddMousePosEvent(mousePos.x, mousePos.y); - } - - // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. - // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. - // - [X] Win32 backend correctly ignore viewports with the _NoInputs flag (here using ::WindowFromPoint with WM_NCHITTEST + HTTRANSPARENT in WndProc does that) - // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window - // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported - // by the backend, and use its flawed heuristic to guess the viewport behind. - // - [X] Win32 backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). - if (hasMouseScreenPos) - { - var viewport = this.ViewportFromPoint(mouseScreenPos); - io.AddMouseViewportEvent(!viewport.IsNull ? viewport.ID : 0u); } else { - io.AddMouseViewportEvent(0); - } - } + if (io.WantSetMousePos) + { + pt.x = (int)io.MousePos.X; + pt.y = (int)io.MousePos.Y; + ClientToScreen(this.hWnd, &pt); + SetCursorPos(pt.x, pt.y); + } - private ImGuiViewportPtr ViewportFromPoint(POINT mouseScreenPos) - { - var hoveredHwnd = WindowFromPoint(mouseScreenPos); - return hoveredHwnd != default ? ImGui.FindViewportByPlatformHandle(hoveredHwnd) : default; + if (GetCursorPos(&pt) && ScreenToClient(this.hWnd, &pt)) + { + io.MousePos.X = pt.x; + io.MousePos.Y = pt.y; + } + else + { + io.MousePos.X = float.MinValue; + io.MousePos.Y = float.MinValue; + } + } } private bool UpdateMouseCursor() @@ -542,7 +451,7 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler return true; } - private void ProcessKeyEventsWorkarounds(HWND focusedWindow) + private void ProcessKeyEventsWorkarounds() { // Left & right Shift keys: when both are pressed together, Windows tend to not generate the WM_KEYUP event for the first released one. if (ImGui.IsKeyDown(ImGuiKey.LeftShift) && !IsVkDown(VK.VK_LSHIFT)) @@ -571,7 +480,7 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler { // See: https://github.com/goatcorp/ImGuiScene/pull/13 // > GetForegroundWindow from winuser.h is a surprisingly expensive function. - var isForeground = focusedWindow == this.hWnd; + var isForeground = GetForegroundWindow() == this.hWnd; for (var i = (int)ImGuiKey.NamedKeyBegin; i < (int)ImGuiKey.NamedKeyEnd; i++) { // Skip raising modifier keys if the game is focused. @@ -713,7 +622,7 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler hbrBackground = (HBRUSH)(1 + COLOR.COLOR_BACKGROUND), lpfnWndProc = (delegate* unmanaged<HWND, uint, WPARAM, LPARAM, LRESULT>)Marshal .GetFunctionPointerForDelegate(this.input.wndProcDelegate), - lpszClassName = windowClassNamePtr, + lpszClassName = (ushort*)windowClassNamePtr, }; if (RegisterClassExW(&wcex) == 0) @@ -737,12 +646,19 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler return; var pio = ImGui.GetPlatformIO(); - ImGui.GetPlatformIO().Handle->Monitors.Free(); + + if (ImGui.GetPlatformIO().Handle->Monitors.Data != null) + { + // We allocated the platform monitor data in OnUpdateMonitors ourselves, + // so we have to free it ourselves to ImGui doesn't try to, or else it will crash + Marshal.FreeHGlobal(new IntPtr(ImGui.GetPlatformIO().Handle->Monitors.Data)); + ImGui.GetPlatformIO().Handle->Monitors = default; + } fixed (char* windowClassNamePtr = WindowClassName) { UnregisterClassW( - windowClassNamePtr, + (ushort*)windowClassNamePtr, (HINSTANCE)Marshal.GetHINSTANCE(typeof(ViewportHandler).Module)); } @@ -777,50 +693,59 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler // Here we use a manual ImVector overload, free the existing monitor data, // and allocate our own, as we are responsible for telling ImGui about monitors var pio = ImGui.GetPlatformIO(); - pio.Handle->Monitors.Resize(0); + var numMonitors = GetSystemMetrics(SM.SM_CMONITORS); + var data = Marshal.AllocHGlobal(Marshal.SizeOf<ImGuiPlatformMonitor>() * numMonitors); + if (pio.Handle->Monitors.Data != null) + Marshal.FreeHGlobal(new IntPtr(pio.Handle->Monitors.Data)); + pio.Handle->Monitors = new(numMonitors, numMonitors, (ImGuiPlatformMonitor*)data.ToPointer()); - EnumDisplayMonitors(default, null, &EnumDisplayMonitorsCallback, default); + // ImGuiPlatformIOPtr platformIO = ImGui.GetPlatformIO(); + // Marshal.FreeHGlobal(platformIO.Handle->Monitors.Data); + // int numMonitors = GetSystemMetrics(SystemMetric.SM_CMONITORS); + // nint data = Marshal.AllocHGlobal(Marshal.SizeOf<ImGuiPlatformMonitor>() * numMonitors); + // platformIO.Handle->Monitors = new ImVector(numMonitors, numMonitors, data); + + var monitorIndex = -1; + var enumfn = new MonitorEnumProcDelegate( + (hMonitor, _, _, _) => + { + monitorIndex++; + var info = new MONITORINFO { cbSize = (uint)sizeof(MONITORINFO) }; + if (!GetMonitorInfoW(hMonitor, &info)) + return true; + + var monitorLt = new Vector2(info.rcMonitor.left, info.rcMonitor.top); + var monitorRb = new Vector2(info.rcMonitor.right, info.rcMonitor.bottom); + var workLt = new Vector2(info.rcWork.left, info.rcWork.top); + var workRb = new Vector2(info.rcWork.right, info.rcWork.bottom); + // Give ImGui the info for this display + + ref var imMonitor = ref ImGui.GetPlatformIO().Monitors.Ref(monitorIndex); + imMonitor.MainPos = monitorLt; + imMonitor.MainSize = monitorRb - monitorLt; + imMonitor.WorkPos = workLt; + imMonitor.WorkSize = workRb - workLt; + imMonitor.DpiScale = 1f; + return true; + }); + EnumDisplayMonitors( + default, + null, + (delegate* unmanaged<HMONITOR, HDC, RECT*, LPARAM, BOOL>)Marshal.GetFunctionPointerForDelegate(enumfn), + default); Log.Information("Monitors set up!"); - foreach (ref var monitor in pio.Handle->Monitors) + for (var i = 0; i < numMonitors; i++) { + var monitor = pio.Handle->Monitors[i]; Log.Information( - "Monitor: {MainPos} {MainSize} {WorkPos} {WorkSize}", + "Monitor {Index}: {MainPos} {MainSize} {WorkPos} {WorkSize}", + i, monitor.MainPos, monitor.MainSize, monitor.WorkPos, monitor.WorkSize); } - - return; - - [UnmanagedCallersOnly] - static BOOL EnumDisplayMonitorsCallback(HMONITOR hMonitor, HDC hdc, RECT* rect, LPARAM lParam) - { - var info = new MONITORINFO { cbSize = (uint)sizeof(MONITORINFO) }; - if (!GetMonitorInfoW(hMonitor, &info)) - return true; - - var monitorLt = new Vector2(info.rcMonitor.left, info.rcMonitor.top); - var monitorRb = new Vector2(info.rcMonitor.right, info.rcMonitor.bottom); - var workLt = new Vector2(info.rcWork.left, info.rcWork.top); - var workRb = new Vector2(info.rcWork.right, info.rcWork.bottom); - - // Give ImGui the info for this display - var imMonitor = new ImGuiPlatformMonitor - { - MainPos = monitorLt, - MainSize = monitorRb - monitorLt, - WorkPos = workLt, - WorkSize = workRb - workLt, - DpiScale = 1f, - }; - if ((info.dwFlags & MONITORINFOF_PRIMARY) != 0) - ImGui.GetPlatformIO().Monitors.PushFront(imMonitor); - else - ImGui.GetPlatformIO().Monitors.PushBack(imMonitor); - return true; - } } [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] @@ -856,8 +781,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler { data->Hwnd = CreateWindowExW( (uint)data->DwExStyle, - windowClassNamePtr, - windowClassNamePtr, + (ushort*)windowClassNamePtr, + (ushort*)windowClassNamePtr, (uint)data->DwStyle, rect.left, rect.top, @@ -869,9 +794,6 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler null); } - if (data->Hwnd == 0) - Util.Fatal($"CreateWindowExW failed: {GetLastError()}", "ImGui Viewport error"); - data->HwndOwned = true; viewport.PlatformRequestResize = false; viewport.PlatformHandle = viewport.PlatformHandleRaw = data->Hwnd; @@ -1071,7 +993,7 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; fixed (char* pwszTitle = MemoryHelper.ReadStringNullTerminated((nint)title)) - SetWindowTextW(data->Hwnd, pwszTitle); + SetWindowTextW(data->Hwnd, (ushort*)pwszTitle); } [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] diff --git a/Dalamud/Interface/ImGuiBackend/Renderers/Dx11Renderer.ViewportHandler.cs b/Dalamud/Interface/ImGuiBackend/Renderers/Dx11Renderer.ViewportHandler.cs index c8d82648e..fe83d58a9 100644 --- a/Dalamud/Interface/ImGuiBackend/Renderers/Dx11Renderer.ViewportHandler.cs +++ b/Dalamud/Interface/ImGuiBackend/Renderers/Dx11Renderer.ViewportHandler.cs @@ -5,7 +5,6 @@ using System.Runtime.InteropServices; using Dalamud.Bindings.ImGui; using Dalamud.Interface.ImGuiBackend.Helpers; using Dalamud.Utility; - using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; diff --git a/Dalamud/Interface/ImGuiBackend/Renderers/Dx11Renderer.cs b/Dalamud/Interface/ImGuiBackend/Renderers/Dx11Renderer.cs index a6abbb862..2eb4e4970 100644 --- a/Dalamud/Interface/ImGuiBackend/Renderers/Dx11Renderer.cs +++ b/Dalamud/Interface/ImGuiBackend/Renderers/Dx11Renderer.cs @@ -15,7 +15,6 @@ using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Textures.TextureWraps.Internal; using Dalamud.Interface.Utility; using Dalamud.Utility; - using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; @@ -31,7 +30,7 @@ namespace Dalamud.Interface.ImGuiBackend.Renderers; Justification = "Multiple fixed/using scopes")] internal unsafe partial class Dx11Renderer : IImGuiRenderer { - private readonly List<IDalamudTextureWrap> fontTextures = []; + private readonly List<IDalamudTextureWrap> fontTextures = new(); private readonly D3D_FEATURE_LEVEL featureLevel; private readonly ViewportHandler viewportHandler; private readonly nint renderNamePtr; @@ -399,9 +398,10 @@ internal unsafe partial class Dx11Renderer : IImGuiRenderer /// </summary> private void CreateFontsTexture() { - ObjectDisposedException.ThrowIf(this.device.IsEmpty(), this); + if (this.device.IsEmpty()) + throw new ObjectDisposedException(nameof(Dx11Renderer)); - if (this.fontTextures.Count != 0) + if (this.fontTextures.Any()) return; var io = ImGui.GetIO(); @@ -479,7 +479,8 @@ internal unsafe partial class Dx11Renderer : IImGuiRenderer /// </summary> private void EnsureDeviceObjects() { - ObjectDisposedException.ThrowIf(this.device.IsEmpty(), this); + if (this.device.IsEmpty()) + throw new ObjectDisposedException(nameof(Dx11Renderer)); var assembly = Assembly.GetExecutingAssembly(); diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialog.Files.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialog.Files.cs index 1bad08f08..e5b7fc15e 100644 --- a/Dalamud/Interface/ImGuiFileDialog/FileDialog.Files.cs +++ b/Dalamud/Interface/ImGuiFileDialog/FileDialog.Files.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading; using Dalamud.Utility; @@ -12,7 +11,7 @@ namespace Dalamud.Interface.ImGuiFileDialog; /// </summary> public partial class FileDialog { - private readonly Lock filesLock = new(); + private readonly object filesLock = new(); private readonly DriveListLoader driveListLoader = new(); diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialog.Filters.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialog.Filters.cs index 46f264354..85b380bee 100644 --- a/Dalamud/Interface/ImGuiFileDialog/FileDialog.Filters.cs +++ b/Dalamud/Interface/ImGuiFileDialog/FileDialog.Filters.cs @@ -8,11 +8,10 @@ namespace Dalamud.Interface.ImGuiFileDialog; /// </summary> public partial class FileDialog { - private List<FilterStruct> filters = []; - private FilterStruct selectedFilter; + private static Regex filterRegex = new(@"[^,{}]+(\{([^{}]*?)\})?", RegexOptions.Compiled); - [GeneratedRegex(@"[^,{}]+(\{([^{}]*?)\})?", RegexOptions.Compiled)] - private static partial Regex FilterRegex(); + private List<FilterStruct> filters = new(); + private FilterStruct selectedFilter; private void ParseFilters(string filters) { @@ -23,13 +22,13 @@ public partial class FileDialog if (filters.Length == 0) return; var currentFilterFound = false; - var matches = FilterRegex().Matches(filters); + var matches = filterRegex.Matches(filters); foreach (Match m in matches) { var match = m.Value; var filter = default(FilterStruct); - if (match.Contains('{')) + if (match.Contains("{")) { var exts = m.Groups[2].Value; filter = new FilterStruct @@ -43,7 +42,7 @@ public partial class FileDialog filter = new FilterStruct { Filter = match, - CollectionFilters = [], + CollectionFilters = new(), }; } @@ -90,7 +89,7 @@ public partial class FileDialog foreach (var file in this.files) { var show = true; - if (!string.IsNullOrEmpty(this.searchBuffer) && !file.FileName.Contains(this.searchBuffer, StringComparison.InvariantCultureIgnoreCase)) + if (!string.IsNullOrEmpty(this.searchBuffer) && !file.FileName.ToLowerInvariant().Contains(this.searchBuffer.ToLowerInvariant())) { show = false; } diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialog.Helpers.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialog.Helpers.cs index 7e5363673..57844c48b 100644 --- a/Dalamud/Interface/ImGuiFileDialog/FileDialog.Helpers.cs +++ b/Dalamud/Interface/ImGuiFileDialog/FileDialog.Helpers.cs @@ -12,7 +12,7 @@ public partial class FileDialog private static string BytesToString(long byteCount) { - string[] suf = [" B", " KB", " MB", " GB", " TB"]; + string[] suf = { " B", " KB", " MB", " GB", " TB" }; if (byteCount == 0) return "0" + suf[0]; var bytes = Math.Abs(byteCount); diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs index b1fc6e049..3c02f9559 100644 --- a/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs +++ b/Dalamud/Interface/ImGuiFileDialog/FileDialog.UI.cs @@ -54,7 +54,7 @@ public partial class FileDialog windowVisible = ImGui.Begin(name, ref this.visible, this.WindowFlags); } - var wasClosed = false; + bool wasClosed = false; if (windowVisible) { if (!this.visible) @@ -122,15 +122,15 @@ public partial class FileDialog { if (iconMap == null) { - iconMap = []; - AddToIconMap(["mp4", "gif", "mov", "avi"], FontAwesomeIcon.FileVideo, miscTextColor); - AddToIconMap(["pdf"], FontAwesomeIcon.FilePdf, miscTextColor); - AddToIconMap(["png", "jpg", "jpeg", "tiff"], FontAwesomeIcon.FileImage, imageTextColor); - AddToIconMap(["cs", "json", "cpp", "h", "py", "xml", "yaml", "js", "html", "css", "ts", "java"], FontAwesomeIcon.FileCode, codeTextColor); - AddToIconMap(["txt", "md"], FontAwesomeIcon.FileAlt, standardTextColor); - AddToIconMap(["zip", "7z", "gz", "tar"], FontAwesomeIcon.FileArchive, miscTextColor); - AddToIconMap(["mp3", "m4a", "ogg", "wav"], FontAwesomeIcon.FileAudio, miscTextColor); - AddToIconMap(["csv"], FontAwesomeIcon.FileCsv, miscTextColor); + iconMap = new(); + AddToIconMap(new[] { "mp4", "gif", "mov", "avi" }, FontAwesomeIcon.FileVideo, miscTextColor); + AddToIconMap(new[] { "pdf" }, FontAwesomeIcon.FilePdf, miscTextColor); + AddToIconMap(new[] { "png", "jpg", "jpeg", "tiff" }, FontAwesomeIcon.FileImage, imageTextColor); + AddToIconMap(new[] { "cs", "json", "cpp", "h", "py", "xml", "yaml", "js", "html", "css", "ts", "java" }, FontAwesomeIcon.FileCode, codeTextColor); + AddToIconMap(new[] { "txt", "md" }, FontAwesomeIcon.FileAlt, standardTextColor); + AddToIconMap(new[] { "zip", "7z", "gz", "tar" }, FontAwesomeIcon.FileArchive, miscTextColor); + AddToIconMap(new[] { "mp3", "m4a", "ogg", "wav" }, FontAwesomeIcon.FileAudio, miscTextColor); + AddToIconMap(new[] { "csv" }, FontAwesomeIcon.FileCsv, miscTextColor); } return iconMap.TryGetValue(ext.ToLowerInvariant(), out var icon) ? icon : new IconColorItem @@ -657,7 +657,6 @@ public partial class FileDialog this.fileNameBuffer = $"{this.selectedFileNames.Count} files Selected"; } - this.SelectionChanged(this, this.GetFilePathName()); if (setLastSelection) { this.lastSelectedFileName = name; diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs index b9ac634ab..3d8246ffd 100644 --- a/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs +++ b/Dalamud/Interface/ImGuiFileDialog/FileDialog.cs @@ -30,7 +30,7 @@ public partial class FileDialog private string currentPath; private string fileNameBuffer = string.Empty; - private List<string> pathDecomposition = []; + private List<string> pathDecomposition = new(); private bool pathClicked = true; private bool pathInputActivated = false; private string pathInputBuffer = string.Empty; @@ -46,12 +46,12 @@ public partial class FileDialog private string searchBuffer = string.Empty; private string lastSelectedFileName = string.Empty; - private List<string> selectedFileNames = []; + private List<string> selectedFileNames = new(); private float footerHeight = 0; private string selectedSideBar = string.Empty; - private List<SideBarItem> quickAccess = []; + private List<SideBarItem> quickAccess = new(); /// <summary> /// Initializes a new instance of the <see cref="FileDialog"/> class. @@ -97,8 +97,6 @@ public partial class FileDialog this.SetupSideBar(); } - public event EventHandler<string>? SelectionChanged; - /// <summary> /// Shows the dialog. /// </summary> @@ -132,12 +130,12 @@ public partial class FileDialog { if (!this.flags.HasFlag(ImGuiFileDialogFlags.SelectOnly)) { - return [this.GetFilePathName()]; + return new List<string> { this.GetFilePathName() }; } if (this.IsDirectoryMode() && this.selectedFileNames.Count == 0) { - return [this.GetFilePathName()]; // current directory + return new List<string> { this.GetFilePathName() }; // current directory } var fullPaths = this.selectedFileNames.Where(x => !string.IsNullOrEmpty(x)).Select(x => Path.Combine(this.currentPath, x)); diff --git a/Dalamud/Interface/ImGuiFileDialog/FileDialogManager.cs b/Dalamud/Interface/ImGuiFileDialog/FileDialogManager.cs index 7332cd735..ee12e7424 100644 --- a/Dalamud/Interface/ImGuiFileDialog/FileDialogManager.cs +++ b/Dalamud/Interface/ImGuiFileDialog/FileDialogManager.cs @@ -30,12 +30,6 @@ public class FileDialogManager private Action<bool, List<string>>? multiCallback; private string savedPath = "."; - /// <summary> - /// Event fires when a new file is selected by the user - /// </summary> - /// <returns>Returns the path of the file as a string</returns> - public event EventHandler<string>? SelectionChanged; - /// <summary> /// Create a dialog which selects an already existing folder. /// </summary> @@ -181,8 +175,6 @@ public class FileDialogManager this.multiCallback = null; } - private void OnSelectionChange(object sender, string path) => this.SelectionChanged?.Invoke(sender, path); - private void SetDialog( string id, string title, @@ -208,7 +200,6 @@ public class FileDialogManager if (this.dialog is not null) { this.dialog.SortOrderChanged -= this.OnSortOrderChange; - this.dialog.SelectionChanged -= this.OnSelectionChange; } this.dialog = new FileDialog(id, title, filters, path, defaultFileName, defaultExtension, selectionCountMax, isModal, flags); @@ -226,7 +217,6 @@ public class FileDialogManager } this.dialog.SortOrderChanged += this.OnSortOrderChange; - this.dialog.SelectionChanged += this.OnSelectionChange; this.dialog.WindowFlags |= this.AddedWindowFlags; foreach (var (name, location, icon, position) in this.CustomSideBarItems) this.dialog.SetQuickAccess(name, location, icon, position); diff --git a/Dalamud/Interface/ImGuiFontChooserDialog/SingleFontChooserDialog.cs b/Dalamud/Interface/ImGuiFontChooserDialog/SingleFontChooserDialog.cs index 2abdd3403..6a381f5b2 100644 --- a/Dalamud/Interface/ImGuiFontChooserDialog/SingleFontChooserDialog.cs +++ b/Dalamud/Interface/ImGuiFontChooserDialog/SingleFontChooserDialog.cs @@ -13,7 +13,6 @@ using Dalamud.Interface.ManagedFontAtlas; using Dalamud.Interface.ManagedFontAtlas.Internals; using Dalamud.Interface.Utility; using Dalamud.Utility; - using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; @@ -32,10 +31,10 @@ public sealed class SingleFontChooserDialog : IDisposable private const float MaxFontSizePt = 127; - private static readonly List<IFontId> EmptyIFontList = []; + private static readonly List<IFontId> EmptyIFontList = new(); private static readonly (string Name, float Value)[] FontSizeList = - [ + { ("9.6", 9.6f), ("10", 10f), ("12", 12f), @@ -52,7 +51,7 @@ public sealed class SingleFontChooserDialog : IDisposable ("46", 46), ("68", 68), ("90", 90), - ]; + }; private static int counterStatic; @@ -1236,7 +1235,7 @@ public sealed class SingleFontChooserDialog : IDisposable } private void UpdateSelectedFamilyAndFontIndices( - List<IFontFamilyId> fonts, + IReadOnlyList<IFontFamilyId> fonts, string familyName, string fontName) { diff --git a/Dalamud/Interface/ImGuiNotification/Internal/NotificationManager.cs b/Dalamud/Interface/ImGuiNotification/Internal/NotificationManager.cs index 1b6e7e59b..340763a55 100644 --- a/Dalamud/Interface/ImGuiNotification/Internal/NotificationManager.cs +++ b/Dalamud/Interface/ImGuiNotification/Internal/NotificationManager.cs @@ -26,8 +26,8 @@ internal class NotificationManager : INotificationManager, IInternalDisposableSe [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get(); - private readonly List<ActiveNotification> notifications = []; - private readonly ConcurrentBag<ActiveNotification> pendingNotifications = []; + private readonly List<ActiveNotification> notifications = new(); + private readonly ConcurrentBag<ActiveNotification> pendingNotifications = new(); private NotificationPositionChooser? positionChooser; diff --git a/Dalamud/Interface/ImGuiNotification/Notification.cs b/Dalamud/Interface/ImGuiNotification/Notification.cs index ef6bb3da8..4dcb10c17 100644 --- a/Dalamud/Interface/ImGuiNotification/Notification.cs +++ b/Dalamud/Interface/ImGuiNotification/Notification.cs @@ -1,5 +1,9 @@ +using System.Threading.Tasks; + using Dalamud.Interface.ImGuiNotification.Internal; using Dalamud.Interface.Textures; +using Dalamud.Interface.Textures.TextureWraps; +using Serilog; namespace Dalamud.Interface.ImGuiNotification; /// <summary>Represents a blueprint for a notification.</summary> diff --git a/Dalamud/Interface/ImGuiNotification/NotificationUtilities.cs b/Dalamud/Interface/ImGuiNotification/NotificationUtilities.cs index 5ef3cf4ac..4f0830fa1 100644 --- a/Dalamud/Interface/ImGuiNotification/NotificationUtilities.cs +++ b/Dalamud/Interface/ImGuiNotification/NotificationUtilities.cs @@ -1,9 +1,11 @@ using System.IO; using System.Numerics; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using Dalamud.Bindings.ImGui; using Dalamud.Game.Text; +using Dalamud.Interface.Internal; using Dalamud.Interface.Internal.Windows; using Dalamud.Interface.ManagedFontAtlas; using Dalamud.Interface.Textures; diff --git a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringColorStackSet.cs b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringColorStackSet.cs index 85ab2e441..ad60d405e 100644 --- a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringColorStackSet.cs +++ b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringColorStackSet.cs @@ -15,6 +15,10 @@ namespace Dalamud.Interface.ImGuiSeStringRenderer.Internal; /// <summary>Color stacks to use while evaluating a SeString.</summary> internal sealed class SeStringColorStackSet { + /// <summary>Parsed <see cref="UIColor"/>, containing colors to use with <see cref="MacroCode.ColorType"/> and + /// <see cref="MacroCode.EdgeColorType"/>.</summary> + private readonly uint[,] colorTypes; + /// <summary>Foreground color stack while evaluating a SeString for rendering.</summary> /// <remarks>Touched only from the main thread.</remarks> private readonly List<uint> colorStack = []; @@ -35,38 +39,30 @@ internal sealed class SeStringColorStackSet foreach (var row in uiColor) maxId = (int)Math.Max(row.RowId, maxId); - this.ColorTypes = new uint[maxId + 1, 4]; + this.colorTypes = new uint[maxId + 1, 4]; foreach (var row in uiColor) { // Contains ABGR. - this.ColorTypes[row.RowId, 0] = row.Dark; - this.ColorTypes[row.RowId, 1] = row.Light; - this.ColorTypes[row.RowId, 2] = row.ClassicFF; - this.ColorTypes[row.RowId, 3] = row.ClearBlue; + this.colorTypes[row.RowId, 0] = row.Dark; + this.colorTypes[row.RowId, 1] = row.Light; + this.colorTypes[row.RowId, 2] = row.ClassicFF; + this.colorTypes[row.RowId, 3] = row.ClearBlue; } if (BitConverter.IsLittleEndian) { // ImGui wants RGBA in LE. - fixed (uint* p = this.ColorTypes) + fixed (uint* p = this.colorTypes) { - foreach (ref var r in new Span<uint>(p, this.ColorTypes.GetLength(0) * this.ColorTypes.GetLength(1))) + foreach (ref var r in new Span<uint>(p, this.colorTypes.GetLength(0) * this.colorTypes.GetLength(1))) r = BinaryPrimitives.ReverseEndianness(r); } } } - /// <summary>Initializes a new instance of the <see cref="SeStringColorStackSet"/> class.</summary> - /// <param name="colorTypes">Color types.</param> - public SeStringColorStackSet(uint[,] colorTypes) => this.ColorTypes = colorTypes; - /// <summary>Gets a value indicating whether at least one color has been pushed to the edge color stack.</summary> public bool HasAdditionalEdgeColor { get; private set; } - /// <summary>Gets the parsed <see cref="UIColor"/> containing colors to use with <see cref="MacroCode.ColorType"/> - /// and <see cref="MacroCode.EdgeColorType"/>.</summary> - public uint[,] ColorTypes { get; } - /// <summary>Resets the colors in the stack.</summary> /// <param name="drawState">Draw state.</param> internal void Initialize(scoped ref SeStringDrawState drawState) @@ -195,9 +191,9 @@ internal sealed class SeStringColorStackSet } // Opacity component is ignored. - var color = themeIndex >= 0 && themeIndex < this.ColorTypes.GetLength(1) && - colorTypeIndex < this.ColorTypes.GetLength(0) - ? this.ColorTypes[colorTypeIndex, themeIndex] + var color = themeIndex >= 0 && themeIndex < this.colorTypes.GetLength(1) && + colorTypeIndex < this.colorTypes.GetLength(0) + ? this.colorTypes[colorTypeIndex, themeIndex] : 0u; rgbaStack.Add(color | 0xFF000000u); diff --git a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs index 5a19f9a50..d0c40cd9f 100644 --- a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs +++ b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs @@ -1,11 +1,9 @@ using System.Collections.Generic; using System.Numerics; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using BitFaster.Caching.Lru; - using Dalamud.Bindings.ImGui; using Dalamud.Data; using Dalamud.Game; @@ -13,10 +11,8 @@ using Dalamud.Game.Text.SeStringHandling; using Dalamud.Interface.ImGuiSeStringRenderer.Internal.TextProcessing; using Dalamud.Interface.Utility; using Dalamud.Utility; - using FFXIVClientStructs.FFXIV.Client.System.String; using FFXIVClientStructs.FFXIV.Client.UI; - using Lumina.Excel.Sheets; using Lumina.Text; using Lumina.Text.Parse; @@ -29,7 +25,7 @@ namespace Dalamud.Interface.ImGuiSeStringRenderer.Internal; /// <summary>Draws SeString.</summary> [ServiceManager.EarlyLoadedService] -internal class SeStringRenderer : IServiceType +internal unsafe class SeStringRenderer : IInternalDisposableService { private const int ImGuiContextCurrentWindowOffset = 0x3FF0; private const int ImGuiWindowDcOffset = 0x118; @@ -51,19 +47,28 @@ internal class SeStringRenderer : IServiceType /// <summary>Parsed text fragments from a SeString.</summary> /// <remarks>Touched only from the main thread.</remarks> - private readonly List<TextFragment> fragmentsMainThread = []; + private readonly List<TextFragment> fragments = []; /// <summary>Color stacks to use while evaluating a SeString for rendering.</summary> /// <remarks>Touched only from the main thread.</remarks> - private readonly SeStringColorStackSet colorStackSetMainThread; + private readonly SeStringColorStackSet colorStackSet; + + /// <summary>Splits a draw list so that different layers of a single glyph can be drawn out of order.</summary> + private ImDrawListSplitter* splitter = ImGui.ImDrawListSplitter(); [ServiceManager.ServiceConstructor] private SeStringRenderer(DataManager dm, TargetSigScanner sigScanner) { - this.colorStackSetMainThread = new(dm.Excel.GetSheet<UIColor>()); + this.colorStackSet = new(dm.Excel.GetSheet<UIColor>()); this.gfd = dm.GetFile<GfdFile>("common/font/gfdata.gfd")!; } + /// <summary>Finalizes an instance of the <see cref="SeStringRenderer"/> class.</summary> + ~SeStringRenderer() => this.ReleaseUnmanagedResources(); + + /// <inheritdoc/> + void IInternalDisposableService.DisposeService() => this.ReleaseUnmanagedResources(); + /// <summary>Compiles and caches a SeString from a text macro representation.</summary> /// <param name="text">SeString text macro representation. /// Newline characters will be normalized to newline payloads.</param> @@ -75,44 +80,6 @@ internal class SeStringRenderer : IServiceType text.ReplaceLineEndings("<br>"), new() { ExceptionMode = MacroStringParseExceptionMode.EmbedError })); - /// <summary>Creates a draw data that will draw the given SeString onto it.</summary> - /// <param name="sss">SeString to render.</param> - /// <param name="drawParams">Parameters for drawing.</param> - /// <returns>A new self-contained draw data.</returns> - public unsafe BufferBackedImDrawData CreateDrawData( - ReadOnlySeStringSpan sss, - scoped in SeStringDrawParams drawParams = default) - { - if (drawParams.TargetDrawList is not null) - { - throw new ArgumentException( - $"{nameof(SeStringDrawParams.TargetDrawList)} may not be specified.", - nameof(drawParams)); - } - - var dd = BufferBackedImDrawData.Create(); - - try - { - var size = this.Draw(sss, drawParams with { TargetDrawList = dd.ListPtr }).Size; - - var offset = drawParams.ScreenOffset ?? Vector2.Zero; - foreach (var vtx in new Span<ImDrawVert>(dd.ListPtr.VtxBuffer.Data, dd.ListPtr.VtxBuffer.Size)) - offset = Vector2.Min(offset, vtx.Pos); - - dd.Data.DisplayPos = offset; - dd.Data.DisplaySize = size - offset; - dd.Data.Valid = 1; - dd.UpdateDrawDataStatistics(); - return dd; - } - catch - { - dd.Dispose(); - throw; - } - } - /// <summary>Compiles and caches a SeString from a text macro representation, and then draws it.</summary> /// <param name="text">SeString text macro representation. /// Newline characters will be normalized to newline payloads.</param> @@ -146,43 +113,28 @@ internal class SeStringRenderer : IServiceType /// <param name="imGuiId">ImGui ID, if link functionality is desired.</param> /// <param name="buttonFlags">Button flags to use on link interaction.</param> /// <returns>Interaction result of the rendered text.</returns> - public unsafe SeStringDrawResult Draw( + public SeStringDrawResult Draw( ReadOnlySeStringSpan sss, scoped in SeStringDrawParams drawParams = default, ImGuiId imGuiId = default, ImGuiButtonFlags buttonFlags = ImGuiButtonFlags.MouseButtonDefault) { - // Interactivity is supported only from the main thread. - if (!imGuiId.IsEmpty()) - ThreadSafety.AssertMainThread(); + // Drawing is only valid if done from the main thread anyway, especially with interactivity. + ThreadSafety.AssertMainThread(); if (drawParams.TargetDrawList is not null && imGuiId) throw new ArgumentException("ImGuiId cannot be set if TargetDrawList is manually set.", nameof(imGuiId)); - using var cleanup = new DisposeSafety.ScopedFinalizer(); - - ImFont* font = null; - if (drawParams.Font.HasValue) - font = drawParams.Font.Value; - - if (ThreadSafety.IsMainThread && drawParams.TargetDrawList is null && font is null) - font = ImGui.GetFont(); - if (font is null) - throw new ArgumentException("Specified font is empty."); - // This also does argument validation for drawParams. Do it here. - // `using var` makes a struct read-only, but we do want to modify it. - using var stateStorage = new SeStringDrawState( - sss, - drawParams, - ThreadSafety.IsMainThread ? this.colorStackSetMainThread : new(this.colorStackSetMainThread.ColorTypes), - ThreadSafety.IsMainThread ? this.fragmentsMainThread : [], - font); - ref var state = ref Unsafe.AsRef(in stateStorage); + var state = new SeStringDrawState(sss, drawParams, this.colorStackSet, this.splitter); + + // Reset and initialize the state. + this.fragments.Clear(); + this.colorStackSet.Initialize(ref state); // Analyze the provided SeString and break it up to text fragments. this.CreateTextFragments(ref state); - var fragmentSpan = CollectionsMarshal.AsSpan(state.Fragments); + var fragmentSpan = CollectionsMarshal.AsSpan(this.fragments); // Calculate size. var size = Vector2.Zero; @@ -195,19 +147,26 @@ internal class SeStringRenderer : IServiceType state.SplitDrawList(); - var itemSize = size; - if (drawParams.TargetDrawList is null) + // Handle cases where ImGui.AlignTextToFramePadding has been called. + var context = ImGui.GetCurrentContext(); + var currLineTextBaseOffset = 0f; + if (!context.IsNull) { - // Handle cases where ImGui.AlignTextToFramePadding has been called. - var currLineTextBaseOffset = ImGui.GetCurrentContext().CurrentWindow.DC.CurrLineTextBaseOffset; - if (currLineTextBaseOffset != 0f) + var currentWindow = context.CurrentWindow; + if (!currentWindow.IsNull) { - itemSize.Y += 2 * currLineTextBaseOffset; - foreach (ref var f in fragmentSpan) - f.Offset += new Vector2(0, currLineTextBaseOffset); + currLineTextBaseOffset = currentWindow.DC.CurrLineTextBaseOffset; } } + var itemSize = size; + if (currLineTextBaseOffset != 0f) + { + itemSize.Y += 2 * currLineTextBaseOffset; + foreach (ref var f in fragmentSpan) + f.Offset += new Vector2(0, currLineTextBaseOffset); + } + // Draw all text fragments. var lastRune = default(Rune); foreach (ref var f in fragmentSpan) @@ -321,6 +280,15 @@ internal class SeStringRenderer : IServiceType return displayRune.Value != 0; } + private void ReleaseUnmanagedResources() + { + if (this.splitter is not null) + { + this.splitter->Destroy(); + this.splitter = null; + } + } + /// <summary>Creates text fragment, taking line and word breaking into account.</summary> /// <param name="state">Draw state.</param> private void CreateTextFragments(ref SeStringDrawState state) @@ -423,7 +391,7 @@ internal class SeStringRenderer : IServiceType var overflows = Math.Max(w, xy.X + fragment.VisibleWidth) > state.WrapWidth; // Test if the fragment does not fit into the current line and the current line is not empty. - if (xy.X != 0 && state.Fragments.Count > 0 && !state.Fragments[^1].BreakAfter && overflows) + if (xy.X != 0 && this.fragments.Count > 0 && !this.fragments[^1].BreakAfter && overflows) { // Introduce break if this is the first time testing the current break unit or the current fragment // is an entity. @@ -433,7 +401,7 @@ internal class SeStringRenderer : IServiceType xy.X = 0; xy.Y += state.LineHeight; w = 0; - CollectionsMarshal.AsSpan(state.Fragments)[^1].BreakAfter = true; + CollectionsMarshal.AsSpan(this.fragments)[^1].BreakAfter = true; fragment.Offset = xy; // Now that the fragment is given its own line, test if it overflows again. @@ -451,16 +419,16 @@ internal class SeStringRenderer : IServiceType fragment = this.CreateFragment(state, prev, curr, true, xy, link, entity, remainingWidth); } } - else if (state.Fragments.Count > 0 && xy.X != 0) + else if (this.fragments.Count > 0 && xy.X != 0) { // New fragment fits into the current line, and it has a previous fragment in the same line. // If the previous fragment ends with a soft hyphen, adjust its width so that the width of its // trailing soft hyphens are not considered. - if (state.Fragments[^1].EndsWithSoftHyphen) - xy.X += state.Fragments[^1].AdvanceWidthWithoutSoftHyphen - state.Fragments[^1].AdvanceWidth; + if (this.fragments[^1].EndsWithSoftHyphen) + xy.X += this.fragments[^1].AdvanceWidthWithoutSoftHyphen - this.fragments[^1].AdvanceWidth; // Adjust this fragment's offset from kerning distance. - xy.X += state.CalculateScaledDistance(state.Fragments[^1].LastRune, fragment.FirstRune); + xy.X += state.CalculateScaledDistance(this.fragments[^1].LastRune, fragment.FirstRune); fragment.Offset = xy; } @@ -471,7 +439,7 @@ internal class SeStringRenderer : IServiceType w = Math.Max(w, xy.X + fragment.VisibleWidth); xy.X += fragment.AdvanceWidth; prev = fragment.To; - state.Fragments.Add(fragment); + this.fragments.Add(fragment); if (fragment.BreakAfter) { @@ -523,7 +491,7 @@ internal class SeStringRenderer : IServiceType if (gfdTextureSrv != 0) { state.Draw( - new(gfdTextureSrv), + new ImTextureID(gfdTextureSrv), offset + new Vector2(x, MathF.Round((state.LineHeight - size.Y) / 2)), size, useHq ? gfdEntry.HqUv0 : gfdEntry.Uv0, @@ -560,7 +528,7 @@ internal class SeStringRenderer : IServiceType return; - static unsafe nint GetGfdTextureSrv() + static nint GetGfdTextureSrv() { var uim = UIModule.Instance(); if (uim is null) @@ -585,7 +553,7 @@ internal class SeStringRenderer : IServiceType /// <summary>Determines a bitmap icon to display for the given SeString payload.</summary> /// <param name="sss">Byte span that should include a SeString payload.</param> /// <returns>Icon to display, or <see cref="None"/> if it should not be displayed as an icon.</returns> - private unsafe BitmapFontIcon GetBitmapFontIconFor(ReadOnlySpan<byte> sss) + private BitmapFontIcon GetBitmapFontIconFor(ReadOnlySpan<byte> sss) { var e = new ReadOnlySeStringSpan(sss).GetEnumerator(); if (!e.MoveNext() || e.Current.MacroCode is not MacroCode.Icon and not MacroCode.Icon2) @@ -742,4 +710,38 @@ internal class SeStringRenderer : IServiceType firstDisplayRune ?? default, lastNonSoftHyphenRune); } + + /// <summary>Represents a text fragment in a SeString span.</summary> + /// <param name="From">Starting byte offset (inclusive) in a SeString.</param> + /// <param name="To">Ending byte offset (exclusive) in a SeString.</param> + /// <param name="Link">Byte offset of the link that decorates this text fragment, or <c>-1</c> if none.</param> + /// <param name="Offset">Offset in pixels w.r.t. <see cref="SeStringDrawParams.ScreenOffset"/>.</param> + /// <param name="Entity">Replacement entity, if any.</param> + /// <param name="VisibleWidth">Visible width of this text fragment. This is the width required to draw everything + /// without clipping.</param> + /// <param name="AdvanceWidth">Advance width of this text fragment. This is the width required to add to the cursor + /// to position the next fragment correctly.</param> + /// <param name="AdvanceWidthWithoutSoftHyphen">Same with <paramref name="AdvanceWidth"/>, but trimming all the + /// trailing soft hyphens.</param> + /// <param name="BreakAfter">Whether to insert a line break after this text fragment.</param> + /// <param name="EndsWithSoftHyphen">Whether this text fragment ends with one or more soft hyphens.</param> + /// <param name="FirstRune">First rune in this text fragment.</param> + /// <param name="LastRune">Last rune in this text fragment, for the purpose of calculating kerning distance with + /// the following text fragment in the same line, if any.</param> + private record struct TextFragment( + int From, + int To, + int Link, + Vector2 Offset, + SeStringReplacementEntity Entity, + float VisibleWidth, + float AdvanceWidth, + float AdvanceWidthWithoutSoftHyphen, + bool BreakAfter, + bool EndsWithSoftHyphen, + Rune FirstRune, + Rune LastRune) + { + public bool IsSoftHyphenVisible => this.EndsWithSoftHyphen && this.BreakAfter; + } } diff --git a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/TextFragment.cs b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/TextFragment.cs deleted file mode 100644 index a64c32109..000000000 --- a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/TextFragment.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Numerics; -using System.Text; - -namespace Dalamud.Interface.ImGuiSeStringRenderer.Internal; - -/// <summary>Represents a text fragment in a SeString span.</summary> -/// <param name="From">Starting byte offset (inclusive) in a SeString.</param> -/// <param name="To">Ending byte offset (exclusive) in a SeString.</param> -/// <param name="Link">Byte offset of the link that decorates this text fragment, or <c>-1</c> if none.</param> -/// <param name="Offset">Offset in pixels w.r.t. <see cref="SeStringDrawParams.ScreenOffset"/>.</param> -/// <param name="Entity">Replacement entity, if any.</param> -/// <param name="VisibleWidth">Visible width of this text fragment. This is the width required to draw everything -/// without clipping.</param> -/// <param name="AdvanceWidth">Advance width of this text fragment. This is the width required to add to the cursor -/// to position the next fragment correctly.</param> -/// <param name="AdvanceWidthWithoutSoftHyphen">Same with <paramref name="AdvanceWidth"/>, but trimming all the -/// trailing soft hyphens.</param> -/// <param name="BreakAfter">Whether to insert a line break after this text fragment.</param> -/// <param name="EndsWithSoftHyphen">Whether this text fragment ends with one or more soft hyphens.</param> -/// <param name="FirstRune">First rune in this text fragment.</param> -/// <param name="LastRune">Last rune in this text fragment, for the purpose of calculating kerning distance with -/// the following text fragment in the same line, if any.</param> -internal record struct TextFragment( - int From, - int To, - int Link, - Vector2 Offset, - SeStringReplacementEntity Entity, - float VisibleWidth, - float AdvanceWidth, - float AdvanceWidthWithoutSoftHyphen, - bool BreakAfter, - bool EndsWithSoftHyphen, - Rune FirstRune, - Rune LastRune) -{ - /// <summary>Gets a value indicating whether the fragment ends with a visible soft hyphen.</summary> - public bool IsSoftHyphenVisible => this.EndsWithSoftHyphen && this.BreakAfter; -} diff --git a/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawParams.cs b/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawParams.cs index 09c3e9ed9..f3d4c44e9 100644 --- a/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawParams.cs +++ b/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawParams.cs @@ -12,11 +12,7 @@ public record struct SeStringDrawParams /// <summary>Gets or sets the target draw list.</summary> /// <value>Target draw list, <c>default(ImDrawListPtr)</c> to not draw, or <c>null</c> to use /// <see cref="ImGui.GetWindowDrawList"/> (the default).</value> - /// <remarks> - /// If this value is set, <see cref="ImGui.Dummy"/> will not be called, and ImGui ID will be ignored. - /// You <b>must</b> specify a valid draw list, a valid font via <see cref="Font"/> and <see cref="FontSize"/> if you set this value, - /// since the renderer will not be able to retrieve them from ImGui context. - /// Must be set when drawing off the main thread. + /// <remarks>If this value is set, <see cref="ImGui.Dummy"/> will not be called, and ImGui ID will be ignored. /// </remarks> public ImDrawListPtr? TargetDrawList { get; set; } @@ -25,20 +21,16 @@ public record struct SeStringDrawParams public SeStringReplacementEntity.GetEntityDelegate? GetEntity { get; set; } /// <summary>Gets or sets the screen offset of the left top corner.</summary> - /// <value>Screen offset to draw at, or <c>null</c> to use <see cref="ImGui.GetCursorScreenPos()"/>, if no <see cref="TargetDrawList"/> - /// is specified. Otherwise, you must specify it (for example, by passing <see cref="ImGui.GetCursorScreenPos()"/> when passing the window - /// draw list.</value> + /// <value>Screen offset to draw at, or <c>null</c> to use <see cref="ImGui.GetCursorScreenPos()"/>.</value> public Vector2? ScreenOffset { get; set; } /// <summary>Gets or sets the font to use.</summary> /// <value>Font to use, or <c>null</c> to use <see cref="ImGui.GetFont"/> (the default).</value> - /// <remarks>Must be set when specifying a target draw-list or drawing off the main thread.</remarks> public ImFontPtr? Font { get; set; } /// <summary>Gets or sets the font size.</summary> /// <value>Font size in pixels, or <c>0</c> to use the current ImGui font size <see cref="ImGui.GetFontSize"/>. /// </value> - /// <remarks>Must be set when specifying a target draw-list or drawing off the main thread.</remarks> public float? FontSize { get; set; } /// <summary>Gets or sets the line height ratio.</summary> diff --git a/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawState.cs b/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawState.cs index ee03643ad..64a7f3db3 100644 --- a/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawState.cs +++ b/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawState.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -7,11 +6,7 @@ using System.Text; using Dalamud.Bindings.ImGui; using Dalamud.Interface.ImGuiSeStringRenderer.Internal; using Dalamud.Interface.Utility; - -using Dalamud.Utility; - using FFXIVClientStructs.FFXIV.Component.GUI; - using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; @@ -19,86 +14,51 @@ namespace Dalamud.Interface.ImGuiSeStringRenderer; /// <summary>Calculated values from <see cref="SeStringDrawParams"/> using ImGui styles.</summary> [StructLayout(LayoutKind.Sequential)] -public unsafe ref struct SeStringDrawState : IDisposable +public unsafe ref struct SeStringDrawState { private static readonly int ChannelCount = Enum.GetValues<SeStringDrawChannel>().Length; private readonly ImDrawList* drawList; - - private ImDrawListSplitter splitter; + private readonly SeStringColorStackSet colorStackSet; + private readonly ImDrawListSplitter* splitter; /// <summary>Initializes a new instance of the <see cref="SeStringDrawState"/> struct.</summary> /// <param name="span">Raw SeString byte span.</param> /// <param name="ssdp">Instance of <see cref="SeStringDrawParams"/> to initialize from.</param> /// <param name="colorStackSet">Instance of <see cref="SeStringColorStackSet"/> to use.</param> - /// <param name="fragments">Fragments.</param> - /// <param name="font">Font to use.</param> + /// <param name="splitter">Instance of ImGui Splitter to use.</param> internal SeStringDrawState( ReadOnlySpan<byte> span, scoped in SeStringDrawParams ssdp, SeStringColorStackSet colorStackSet, - List<TextFragment> fragments, - ImFont* font) + ImDrawListSplitter* splitter) { + this.colorStackSet = colorStackSet; + this.splitter = splitter; + this.drawList = ssdp.TargetDrawList ?? ImGui.GetWindowDrawList(); this.Span = span; - this.ColorStackSet = colorStackSet; - this.Fragments = fragments; - this.Font = font; - - if (ssdp.TargetDrawList is null) - { - if (!ThreadSafety.IsMainThread) - { - throw new ArgumentException( - $"{nameof(ssdp.TargetDrawList)} must be set to render outside the main thread."); - } - - this.drawList = ssdp.TargetDrawList ?? ImGui.GetWindowDrawList(); - this.ScreenOffset = ssdp.ScreenOffset ?? ImGui.GetCursorScreenPos(); - this.FontSize = ssdp.FontSize ?? ImGui.GetFontSize(); - this.WrapWidth = ssdp.WrapWidth ?? ImGui.GetContentRegionAvail().X; - this.Color = ssdp.Color ?? ImGui.GetColorU32(ImGuiCol.Text); - this.LinkHoverBackColor = ssdp.LinkHoverBackColor ?? ImGui.GetColorU32(ImGuiCol.ButtonHovered); - this.LinkActiveBackColor = ssdp.LinkActiveBackColor ?? ImGui.GetColorU32(ImGuiCol.ButtonActive); - this.ThemeIndex = ssdp.ThemeIndex ?? AtkStage.Instance()->AtkUIColorHolder->ActiveColorThemeType; - } - else - { - this.drawList = ssdp.TargetDrawList.Value; - this.ScreenOffset = ssdp.ScreenOffset ?? Vector2.Zero; - - this.ScreenOffset = ssdp.ScreenOffset ?? throw new ArgumentException( - $"{nameof(ssdp.ScreenOffset)} must be set when specifying a target draw list, as it cannot be fetched from the ImGui state. (GetCursorScreenPos?)"); - this.FontSize = ssdp.FontSize ?? throw new ArgumentException( - $"{nameof(ssdp.FontSize)} must be set when specifying a target draw list, as it cannot be fetched from the ImGui state."); - - // this.FontSize = ssdp.FontSize ?? throw new ArgumentException( - // $"{nameof(ssdp.FontSize)} must be set when specifying a target draw list, as it cannot be fetched from the ImGui state."); - this.WrapWidth = ssdp.WrapWidth ?? float.MaxValue; - this.Color = ssdp.Color ?? uint.MaxValue; - this.LinkHoverBackColor = 0; // Interactivity is unused outside the main thread. - this.LinkActiveBackColor = 0; // Interactivity is unused outside the main thread. - this.ThemeIndex = ssdp.ThemeIndex ?? 0; - } - - this.splitter = default; this.GetEntity = ssdp.GetEntity; + this.ScreenOffset = ssdp.ScreenOffset ?? ImGui.GetCursorScreenPos(); this.ScreenOffset = new(MathF.Round(this.ScreenOffset.X), MathF.Round(this.ScreenOffset.Y)); - this.FontSizeScale = this.FontSize / this.Font.FontSize; + this.Font = ssdp.EffectiveFont; + this.FontSize = ssdp.FontSize ?? ImGui.GetFontSize(); + this.FontSizeScale = this.FontSize / this.Font->FontSize; this.LineHeight = MathF.Round(ssdp.EffectiveLineHeight); + this.WrapWidth = ssdp.WrapWidth ?? ImGui.GetContentRegionAvail().X; this.LinkUnderlineThickness = ssdp.LinkUnderlineThickness ?? 0f; this.Opacity = ssdp.EffectiveOpacity; this.EdgeOpacity = (ssdp.EdgeStrength ?? 0.25f) * ssdp.EffectiveOpacity; + this.Color = ssdp.Color ?? ImGui.GetColorU32(ImGuiCol.Text); this.EdgeColor = ssdp.EdgeColor ?? 0xFF000000; this.ShadowColor = ssdp.ShadowColor ?? 0xFF000000; + this.LinkHoverBackColor = ssdp.LinkHoverBackColor ?? ImGui.GetColorU32(ImGuiCol.ButtonHovered); + this.LinkActiveBackColor = ssdp.LinkActiveBackColor ?? ImGui.GetColorU32(ImGuiCol.ButtonActive); this.ForceEdgeColor = ssdp.ForceEdgeColor; + this.ThemeIndex = ssdp.ThemeIndex ?? AtkStage.Instance()->AtkUIColorHolder->ActiveColorThemeType; this.Bold = ssdp.Bold; this.Italic = ssdp.Italic; this.Edge = ssdp.Edge; this.Shadow = ssdp.Shadow; - - this.ColorStackSet.Initialize(ref this); - fragments.Clear(); } /// <inheritdoc cref="SeStringDrawParams.TargetDrawList"/> @@ -114,7 +74,7 @@ public unsafe ref struct SeStringDrawState : IDisposable public Vector2 ScreenOffset { get; } /// <inheritdoc cref="SeStringDrawParams.Font"/> - public ImFontPtr Font { get; } + public ImFont* Font { get; } /// <inheritdoc cref="SeStringDrawParams.FontSize"/> public float FontSize { get; } @@ -175,7 +135,7 @@ public unsafe ref struct SeStringDrawState : IDisposable /// <summary>Gets a value indicating whether the edge should be drawn.</summary> public readonly bool ShouldDrawEdge => - (this.Edge || this.ColorStackSet.HasAdditionalEdgeColor) && this.EdgeColor >= 0x1000000; + (this.Edge || this.colorStackSet.HasAdditionalEdgeColor) && this.EdgeColor >= 0x1000000; /// <summary>Gets a value indicating whether the edge should be drawn.</summary> public readonly bool ShouldDrawShadow => this is { Shadow: true, ShadowColor: >= 0x1000000 }; @@ -183,20 +143,11 @@ public unsafe ref struct SeStringDrawState : IDisposable /// <summary>Gets a value indicating whether the edge should be drawn.</summary> public readonly bool ShouldDrawForeground => this is { Color: >= 0x1000000 }; - /// <summary>Gets the color stacks.</summary> - internal SeStringColorStackSet ColorStackSet { get; } - - /// <summary>Gets the text fragments.</summary> - internal List<TextFragment> Fragments { get; } - - /// <inheritdoc/> - public void Dispose() => this.splitter.ClearFreeMemory(); - /// <summary>Sets the current channel in the ImGui draw list splitter.</summary> /// <param name="channelIndex">Channel to switch to.</param> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetCurrentChannel(SeStringDrawChannel channelIndex) => - this.splitter.SetCurrentChannel(this.drawList, (int)channelIndex); + public readonly void SetCurrentChannel(SeStringDrawChannel channelIndex) => + this.splitter->SetCurrentChannel(this.drawList, (int)channelIndex); /// <summary>Draws a single texture.</summary> /// <param name="igTextureId">ImGui texture ID to draw from.</param> @@ -265,9 +216,9 @@ public unsafe ref struct SeStringDrawState : IDisposable /// <summary>Draws a single glyph using current styling configurations.</summary> /// <param name="g">Glyph to draw.</param> /// <param name="offset">Offset of the glyph in pixels w.r.t. <see cref="ScreenOffset"/>.</param> - internal void DrawGlyph(scoped in ImGuiHelpers.ImFontGlyphReal g, Vector2 offset) + internal readonly void DrawGlyph(scoped in ImGuiHelpers.ImFontGlyphReal g, Vector2 offset) { - var texId = this.Font.ContainerAtlas.Textures.Ref<ImFontAtlasTexture>(g.TextureIndex).TexID; + var texId = this.Font->ContainerAtlas->Textures.Ref<ImFontAtlasTexture>(g.TextureIndex).TexID; var xy0 = new Vector2( MathF.Round(g.X0 * this.FontSizeScale), MathF.Round(g.Y0 * this.FontSizeScale)); @@ -317,14 +268,14 @@ public unsafe ref struct SeStringDrawState : IDisposable /// <param name="offset">Offset of the glyph in pixels w.r.t. /// <see cref="SeStringDrawParams.ScreenOffset"/>.</param> /// <param name="advanceWidth">Advance width of the glyph.</param> - internal void DrawLinkUnderline(Vector2 offset, float advanceWidth) + internal readonly void DrawLinkUnderline(Vector2 offset, float advanceWidth) { if (this.LinkUnderlineThickness < 1f) return; offset += this.ScreenOffset; offset.Y += (this.LinkUnderlineThickness - 1) / 2f; - offset.Y += MathF.Round(((this.LineHeight - this.FontSize) / 2) + (this.Font.Ascent * this.FontSizeScale)); + offset.Y += MathF.Round(((this.LineHeight - this.FontSize) / 2) + (this.Font->Ascent * this.FontSizeScale)); this.SetCurrentChannel(SeStringDrawChannel.Foreground); this.DrawList.AddLine( @@ -351,9 +302,9 @@ public unsafe ref struct SeStringDrawState : IDisposable internal readonly ref ImGuiHelpers.ImFontGlyphReal FindGlyph(Rune rune) { var p = rune.Value is >= ushort.MinValue and < ushort.MaxValue - ? (ImFontGlyphPtr)this.Font.FindGlyph((ushort)rune.Value) - : this.Font.FallbackGlyph; - return ref *(ImGuiHelpers.ImFontGlyphReal*)p.Handle; + ? this.Font->FindGlyph((ushort)rune.Value) + : this.Font->FallbackGlyph; + return ref *(ImGuiHelpers.ImFontGlyphReal*)p; } /// <summary>Gets the glyph corresponding to the given codepoint.</summary> @@ -386,7 +337,7 @@ public unsafe ref struct SeStringDrawState : IDisposable return 0; return MathF.Round( - this.Font.GetDistanceAdjustmentForPair( + this.Font->GetDistanceAdjustmentForPair( (ushort)left.Value, (ushort)right.Value) * this.FontSizeScale); } @@ -399,15 +350,15 @@ public unsafe ref struct SeStringDrawState : IDisposable switch (payload.MacroCode) { case MacroCode.Color: - this.ColorStackSet.HandleColorPayload(ref this, payload); + this.colorStackSet.HandleColorPayload(ref this, payload); return true; case MacroCode.EdgeColor: - this.ColorStackSet.HandleEdgeColorPayload(ref this, payload); + this.colorStackSet.HandleEdgeColorPayload(ref this, payload); return true; case MacroCode.ShadowColor: - this.ColorStackSet.HandleShadowColorPayload(ref this, payload); + this.colorStackSet.HandleShadowColorPayload(ref this, payload); return true; case MacroCode.Bold when payload.TryGetExpression(out var e) && e.TryGetUInt(out var u): @@ -428,11 +379,11 @@ public unsafe ref struct SeStringDrawState : IDisposable return true; case MacroCode.ColorType: - this.ColorStackSet.HandleColorTypePayload(ref this, payload); + this.colorStackSet.HandleColorTypePayload(ref this, payload); return true; case MacroCode.EdgeColorType: - this.ColorStackSet.HandleEdgeColorTypePayload(ref this, payload); + this.colorStackSet.HandleEdgeColorTypePayload(ref this, payload); return true; default: @@ -442,9 +393,10 @@ public unsafe ref struct SeStringDrawState : IDisposable /// <summary>Splits the draw list.</summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void SplitDrawList() => this.splitter.Split(this.drawList, ChannelCount); + internal readonly void SplitDrawList() => + this.splitter->Split(this.drawList, ChannelCount); /// <summary>Merges the draw list.</summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void MergeDrawList() => this.splitter.Merge(this.drawList); + internal readonly void MergeDrawList() => this.splitter->Merge(this.drawList); } diff --git a/Dalamud/Interface/Internal/Asserts/AssertHandler.cs b/Dalamud/Interface/Internal/Asserts/AssertHandler.cs index fadf80406..276dddb57 100644 --- a/Dalamud/Interface/Internal/Asserts/AssertHandler.cs +++ b/Dalamud/Interface/Internal/Asserts/AssertHandler.cs @@ -21,7 +21,7 @@ internal class AssertHandler : IDisposable private const int HidePrintEvery = 500; private readonly HashSet<string> ignoredAsserts = []; - private readonly Dictionary<string, uint> assertCounts = []; + private readonly Dictionary<string, uint> assertCounts = new(); // Store callback to avoid it from being GC'd private readonly AssertCallbackDelegate callback; diff --git a/Dalamud/Interface/Internal/Badge/BadgeInfo.cs b/Dalamud/Interface/Internal/Badge/BadgeInfo.cs deleted file mode 100644 index 0787f0658..000000000 --- a/Dalamud/Interface/Internal/Badge/BadgeInfo.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Numerics; - -namespace Dalamud.Interface.Internal.Badge; - -/// <summary> -/// Represents information about a badge. -/// </summary> -/// <param name="Name">Name of the badge.</param> -/// <param name="Description">Description of the badge.</param> -/// <param name="IconIndex">Icon index.</param> -/// <param name="UnlockSha256">Sha256 hash of the unlock password.</param> -/// <param name="UnlockMethod">How the badge is unlocked.</param> -internal record BadgeInfo( - Func<string> Name, - Func<string> Description, - int IconIndex, - string UnlockSha256, - BadgeUnlockMethod UnlockMethod) -{ - private const float BadgeWidth = 256; - private const float BadgeHeight = 256; - private const float BadgesPerRow = 2; - - /// <summary> - /// Gets the UV coordinates for the badge icon in the atlas. - /// </summary> - /// <param name="atlasWidthPx">Width of the atlas.</param> - /// <param name="atlasHeightPx">Height of the atlas.</param> - /// <returns>UV coordinates.</returns> - public (Vector2 Uv0, Vector2 Uv1) GetIconUv(float atlasWidthPx, float atlasHeightPx) - { - // Calculate row and column from icon index - var col = this.IconIndex % (int)BadgesPerRow; - var row = this.IconIndex / (int)BadgesPerRow; - - // Calculate pixel positions - var x0 = col * BadgeWidth; - var y0 = row * BadgeHeight; - var x1 = x0 + BadgeWidth; - var y1 = y0 + BadgeHeight; - - // Convert to UV coordinates (0.0 to 1.0) - var uv0 = new Vector2(x0 / atlasWidthPx, y0 / atlasHeightPx); - var uv1 = new Vector2(x1 / atlasWidthPx, y1 / atlasHeightPx); - - return (uv0, uv1); - } -} diff --git a/Dalamud/Interface/Internal/Badge/BadgeManager.cs b/Dalamud/Interface/Internal/Badge/BadgeManager.cs deleted file mode 100644 index 9290d6cc8..000000000 --- a/Dalamud/Interface/Internal/Badge/BadgeManager.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -using Dalamud.Configuration.Internal; - -namespace Dalamud.Interface.Internal.Badge; - -/// <summary> -/// Service responsible for managing user badges. -/// </summary> -[ServiceManager.EarlyLoadedService] -internal class BadgeManager : IServiceType -{ - private readonly DalamudConfiguration configuration; - - private readonly List<BadgeInfo> badges = - [ - new(() => "Test Badge", - () => "Awarded for testing badges.", - 0, - "937e8d5fbb48bd4949536cd65b8d35c426b80d2f830c5c308e2cdec422ae2244", - BadgeUnlockMethod.User), - - new(() => "Fundraiser #1 Donor", - () => "Awarded for participating in the first patch fundraiser.", - 1, - "56e752257bd0cbb2944f95cc7b3cb3d0db15091dd043f7a195ed37028d079322", - BadgeUnlockMethod.User) - ]; - - private readonly List<int> unlockedBadgeIndices = []; - - /// <summary> - /// Initializes a new instance of the <see cref="BadgeManager"/> class. - /// </summary> - /// <param name="configuration">Configuration to use.</param> - [ServiceManager.ServiceConstructor] - public BadgeManager(DalamudConfiguration configuration) - { - this.configuration = configuration; - - foreach (var usedBadge in this.configuration.UsedBadgePasswords) - { - this.TryUnlockBadge(usedBadge, BadgeUnlockMethod.Startup, out _); - } - } - - /// <summary> - /// Gets the badges the user has unlocked. - /// </summary> - public IEnumerable<BadgeInfo> UnlockedBadges - => this.badges.Where((_, index) => this.unlockedBadgeIndices.Contains(index)); - - /// <summary> - /// Unlock a badge with the given password and method. - /// </summary> - /// <param name="password">The password to unlock the badge with.</param> - /// <param name="method">How we are unlocking this badge.</param> - /// <param name="unlockedBadge">The badge that was unlocked, if the function returns true, null otherwise.</param> - /// <returns>The unlocked badge, if one was unlocked by this call.</returns> - public bool TryUnlockBadge(string password, BadgeUnlockMethod method, out BadgeInfo unlockedBadge) - { - var sha256 = System.Security.Cryptography.SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(password)); - var hashString = Convert.ToHexString(sha256); - - foreach (var (idx, badge) in this.badges.Where(x => x.UnlockMethod == method || method == BadgeUnlockMethod.Startup).Index()) - { - if (!this.unlockedBadgeIndices.Contains(idx) && badge.UnlockSha256.Equals(hashString, StringComparison.OrdinalIgnoreCase)) - { - if (method != BadgeUnlockMethod.Startup) - { - this.configuration.UsedBadgePasswords.Add(password); - this.configuration.QueueSave(); - } - - this.unlockedBadgeIndices.Add(idx); - unlockedBadge = badge; - return true; - } - } - - unlockedBadge = null!; - return false; - } -} diff --git a/Dalamud/Interface/Internal/Badge/BadgeUnlockMethod.cs b/Dalamud/Interface/Internal/Badge/BadgeUnlockMethod.cs deleted file mode 100644 index 45828c097..000000000 --- a/Dalamud/Interface/Internal/Badge/BadgeUnlockMethod.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Dalamud.Interface.Internal.Badge; - -/// <summary> -/// Method by which a badge can be unlocked. -/// </summary> -internal enum BadgeUnlockMethod -{ - /// <summary> - /// Badge can be unlocked by the user by entering a password. - /// </summary> - User, - - /// <summary> - /// Badge can be unlocked from Dalamud internal features. - /// </summary> - Internal, - - /// <summary> - /// Badge is no longer obtainable and can only be unlocked from the configuration file. - /// </summary> - Startup, -} diff --git a/Dalamud/Interface/Internal/DalamudCommands.cs b/Dalamud/Interface/Internal/DalamudCommands.cs index 9812a4e6a..b1fdb5232 100644 --- a/Dalamud/Interface/Internal/DalamudCommands.cs +++ b/Dalamud/Interface/Internal/DalamudCommands.cs @@ -1,9 +1,9 @@ +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using CheapLoc; - using Dalamud.Configuration.Internal; using Dalamud.Game; using Dalamud.Game.Command; @@ -11,6 +11,7 @@ using Dalamud.Game.Gui; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Plugin.Internal; using Dalamud.Utility; +using Serilog; namespace Dalamud.Interface.Internal; @@ -207,7 +208,7 @@ internal class DalamudCommands : IServiceType var chatGui = Service<ChatGui>.Get(); var configuration = Service<DalamudConfiguration>.Get(); - configuration.BadWords ??= []; + configuration.BadWords ??= new List<string>(); if (configuration.BadWords.Count == 0) { @@ -226,7 +227,7 @@ internal class DalamudCommands : IServiceType var chatGui = Service<ChatGui>.Get(); var configuration = Service<DalamudConfiguration>.Get(); - configuration.BadWords ??= []; + configuration.BadWords ??= new List<string>(); configuration.BadWords.RemoveAll(x => x == arguments); @@ -304,12 +305,12 @@ internal class DalamudCommands : IServiceType chatGui.Print(new SeStringBuilder() .AddItalics("Dalamud:") - .AddText($" {Versioning.GetScmVersion()}") + .AddText($" {Util.GetScmVersion()}") .Build()); chatGui.Print(new SeStringBuilder() .AddItalics("FFXIVCS:") - .AddText($" {Versioning.GetGitHashClientStructs()}") + .AddText($" {Util.GetGitHashClientStructs()}") .Build()); } @@ -325,7 +326,7 @@ internal class DalamudCommands : IServiceType var configuration = Service<DalamudConfiguration>.Get(); var localization = Service<Localization>.Get(); - if (Localization.ApplicableLangCodes.Contains(arguments.ToLowerInvariant()) || arguments.Equals("en", StringComparison.InvariantCultureIgnoreCase)) + if (Localization.ApplicableLangCodes.Contains(arguments.ToLowerInvariant()) || arguments.ToLowerInvariant() == "en") { localization.SetupWithLangCode(arguments.ToLowerInvariant()); configuration.LanguageOverride = arguments.ToLowerInvariant(); diff --git a/Dalamud/Interface/Internal/DalamudIme.cs b/Dalamud/Interface/Internal/DalamudIme.cs index e5ff83ff8..cdb976333 100644 --- a/Dalamud/Interface/Internal/DalamudIme.cs +++ b/Dalamud/Interface/Internal/DalamudIme.cs @@ -74,7 +74,7 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService private readonly ImGuiSetPlatformImeDataDelegate setPlatformImeDataDelegate; /// <summary>The candidates.</summary> - private readonly List<(string String, bool Supported)> candidateStrings = []; + private readonly List<(string String, bool Supported)> candidateStrings = new(); /// <summary>The selected imm component.</summary> private string compositionString = string.Empty; diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs index 43f6d6ce7..202334580 100644 --- a/Dalamud/Interface/Internal/DalamudInterface.cs +++ b/Dalamud/Interface/Internal/DalamudInterface.cs @@ -7,7 +7,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImPlot; using Dalamud.Configuration.Internal; @@ -20,9 +19,6 @@ using Dalamud.Game.Gui; using Dalamud.Hooking; using Dalamud.Interface.Animation.EasingFunctions; using Dalamud.Interface.Colors; -using Dalamud.Interface.ImGuiNotification; -using Dalamud.Interface.ImGuiNotification.Internal; -using Dalamud.Interface.Internal.Badge; using Dalamud.Interface.Internal.Windows; using Dalamud.Interface.Internal.Windows.Data; using Dalamud.Interface.Internal.Windows.PluginInstaller; @@ -40,11 +36,9 @@ using Dalamud.Plugin.Internal; using Dalamud.Plugin.SelfTest.Internal; using Dalamud.Storage.Assets; using Dalamud.Utility; - using FFXIVClientStructs.FFXIV.Client.System.Framework; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Component.GUI; - using Serilog.Events; namespace Dalamud.Interface.Internal; @@ -57,7 +51,7 @@ internal class DalamudInterface : IInternalDisposableService { private const float CreditsDarkeningMaxAlpha = 0.8f; - private static readonly ModuleLog Log = ModuleLog.Create<DalamudInterface>(); + private static readonly ModuleLog Log = new("DUI"); private readonly Dalamud dalamud; private readonly DalamudConfiguration configuration; @@ -188,7 +182,7 @@ internal class DalamudInterface : IInternalDisposableService () => Service<DalamudInterface>.GetNullable()?.ToggleDevMenu(), VirtualKey.SHIFT); - if (Versioning.GetActiveTrack() != "release") + if (!configuration.DalamudBetaKind.IsNullOrEmpty()) { titleScreenMenu.AddEntryCore( Loc.Localize("TSMDalamudDevMenu", "Developer Menu"), @@ -539,32 +533,6 @@ internal class DalamudInterface : IInternalDisposableService this.creditsDarkeningAnimation.Restart(); } - /// <inheritdoc cref="DataWindow.GetWidget{T}"/> - public T GetDataWindowWidget<T>() where T : IDataWindowWidget => this.dataWindow.GetWidget<T>(); - - /// <summary>Sets the data window current widget.</summary> - /// <param name="widget">Widget to set current.</param> - public void SetDataWindowWidget(IDataWindowWidget widget) => this.dataWindow.CurrentWidget = widget; - - /// <summary> - /// Play an animation when a badge has been unlocked. - /// </summary> - /// <param name="badge">The badge that has been unlocked.</param> - public void StartBadgeUnlockAnimation(BadgeInfo badge) - { - var badgeTexture = Service<DalamudAssetManager>.Get().GetDalamudTextureWrap(DalamudAsset.BadgeAtlas); - var uvs = badge.GetIconUv(badgeTexture.Width, badgeTexture.Height); - - // TODO: Make it more fancy? - Service<NotificationManager>.Get().AddNotification( - new Notification - { - Title = "Badge unlocked!", - Content = $"You unlocked the badge '{badge.Name()}'", - Type = NotificationType.Success, - }); - } - private void OnDraw() { this.FrameCount++; @@ -586,7 +554,6 @@ internal class DalamudInterface : IInternalDisposableService { this.DrawHiddenDevMenuOpener(); this.DrawDevMenu(); - this.DrawTitleScreenBadges(); if (Service<GameGui>.Get().GameUiHidden) return; @@ -617,68 +584,6 @@ internal class DalamudInterface : IInternalDisposableService } } - private void DrawTitleScreenBadges() - { - if (!this.titleScreenMenuWindow.IsOpen) - return; - - var badgeManager = Service<BadgeManager>.Get(); - if (!this.configuration.ShowBadgesOnTitleScreen || !badgeManager.UnlockedBadges.Any()) - return; - - var vp = ImGui.GetMainViewport(); - ImGui.SetNextWindowPos(vp.Pos); - ImGui.SetNextWindowSize(vp.Size); - ImGuiHelpers.ForceNextWindowMainViewport(); - ImGui.SetNextWindowBgAlpha(0f); - - ImGui.Begin( - "###TitleScreenBadgeWindow"u8, - ImGuiWindowFlags.NoInputs | ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoMove | - ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoBringToFrontOnFocus | - ImGuiWindowFlags.NoNav); - - var badgeAtlas = Service<DalamudAssetManager>.Get().GetDalamudTextureWrap(DalamudAsset.BadgeAtlas); - var badgeSize = ImGuiHelpers.GlobalScale * 80; - var spacing = ImGuiHelpers.GlobalScale * 10; - const float margin = 60f; - var startPos = vp.Pos + new Vector2(vp.Size.X - margin, margin); - - // Use the mouse position in screen space for hover detection because the usual ImGui hover checks - // don't work with this full-viewport overlay window setup. - var mouse = ImGui.GetMousePos(); - - foreach (var badge in badgeManager.UnlockedBadges) - { - var uvs = badge.GetIconUv(badgeAtlas.Width, badgeAtlas.Height); - - startPos.X -= badgeSize; - ImGui.SetCursorPos(startPos); - ImGui.Image(badgeAtlas.Handle, new Vector2(badgeSize), uvs.Uv0, uvs.Uv1); - - // Get the actual screen-space bounds of the image we just drew - var badgeMin = ImGui.GetItemRectMin(); - var badgeMax = ImGui.GetItemRectMax(); - - // add spacing to the left for the next badge - startPos.X -= spacing; - - // Manual hit test using mouse position - if (mouse.X >= badgeMin.X && mouse.X <= badgeMax.X && mouse.Y >= badgeMin.Y && mouse.Y <= badgeMax.Y) - { - ImGui.BeginTooltip(); - ImGui.PushTextWrapPos(300 * ImGuiHelpers.GlobalScale); - ImGui.TextWrapped(badge.Name()); - ImGui.Separator(); - ImGui.TextColoredWrapped(ImGuiColors.DalamudGrey, badge.Description()); - ImGui.PopTextWrapPos(); - ImGui.EndTooltip(); - } - } - - ImGui.End(); - } - private void DrawCreditsDarkeningAnimation() { using var style1 = ImRaii.PushStyle(ImGuiStyleVar.WindowRounding, 0f); @@ -755,10 +660,6 @@ internal class DalamudInterface : IInternalDisposableService { if (this.isImGuiDrawDevMenu) { - using var barColor = ImRaii.PushColor(ImGuiCol.WindowBg, new Vector4(0.060f, 0.060f, 0.060f, 0.773f)); - barColor.Push(ImGuiCol.MenuBarBg, Vector4.Zero); - barColor.Push(ImGuiCol.Border, Vector4.Zero); - barColor.Push(ImGuiCol.BorderShadow, Vector4.Zero); if (ImGui.BeginMainMenuBar()) { var pluginManager = Service<PluginManager>.Get(); @@ -782,7 +683,7 @@ internal class DalamudInterface : IInternalDisposableService if (ImGui.BeginMenu("Set log level..."u8)) { - foreach (var logLevel in Enum.GetValues<LogEventLevel>()) + foreach (var logLevel in Enum.GetValues(typeof(LogEventLevel)).Cast<LogEventLevel>()) { if (ImGui.MenuItem(logLevel + "##logLevelSwitch", (byte*)null, EntryPoint.LogLevelSwitch.MinimumLevel == logLevel)) { @@ -915,7 +816,7 @@ internal class DalamudInterface : IInternalDisposableService if (ImGui.MenuItem("Cause CLR fastfail"u8)) { - static unsafe void CauseFastFail() + unsafe void CauseFastFail() { // ReSharper disable once NotAccessedVariable var texture = Unsafe.AsRef<AtkTexture>((void*)0x12345678); @@ -931,11 +832,6 @@ internal class DalamudInterface : IInternalDisposableService ImGui.PopStyleVar(); } - if (ImGui.MenuItem("Raise external event through boot")) - { - ErrorHandling.CrashWithContext("Tést"); - } - ImGui.EndMenu(); } @@ -953,7 +849,7 @@ internal class DalamudInterface : IInternalDisposableService } ImGui.MenuItem(this.dalamud.StartInfo.GameVersion?.ToString() ?? "Unknown version", false, false); - ImGui.MenuItem($"D: {Versioning.GetScmVersion()} CS: {Versioning.GetGitHashClientStructs()}[{FFXIVClientStructs.ThisAssembly.Git.Commits}]", false, false); + ImGui.MenuItem($"D: {Util.GetScmVersion()} CS: {Util.GetGitHashClientStructs()}[{FFXIVClientStructs.ThisAssembly.Git.Commits}]", false, false); ImGui.MenuItem($"CLR: {Environment.Version}", false, false); ImGui.EndMenu(); @@ -1164,8 +1060,8 @@ internal class DalamudInterface : IInternalDisposableService { ImGui.PushFont(InterfaceManager.MonoFont); - ImGui.BeginMenu($"{Versioning.GetActiveTrack() ?? "???"} on {Versioning.GetGitBranch() ?? "???"}", false); - ImGui.BeginMenu($"{Versioning.GetScmVersion()}", false); + ImGui.BeginMenu($"{Util.GetActiveTrack() ?? "???"} on {Util.GetGitBranch() ?? "???"}", false); + ImGui.BeginMenu($"{Util.GetScmVersion()}", false); ImGui.BeginMenu(this.FrameCount.ToString("000000"), false); ImGui.BeginMenu(ImGui.GetIO().Framerate.ToString("000"), false); ImGui.BeginMenu($"W:{Util.FormatBytes(GC.GetTotalMemory(false))}", false); diff --git a/Dalamud/Interface/Internal/DesignSystem/DalamudComponents.PluginPicker.cs b/Dalamud/Interface/Internal/DesignSystem/DalamudComponents.PluginPicker.cs index 3a69d55ea..33bcffd38 100644 --- a/Dalamud/Interface/Internal/DesignSystem/DalamudComponents.PluginPicker.cs +++ b/Dalamud/Interface/Internal/DesignSystem/DalamudComponents.PluginPicker.cs @@ -1,8 +1,7 @@ -using System.Linq; +using System.Linq; using System.Numerics; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; diff --git a/Dalamud/Interface/Internal/ImGuiClipboardFunctionProvider.cs b/Dalamud/Interface/Internal/ImGuiClipboardFunctionProvider.cs index 13623545c..b7bfd21f1 100644 --- a/Dalamud/Interface/Internal/ImGuiClipboardFunctionProvider.cs +++ b/Dalamud/Interface/Internal/ImGuiClipboardFunctionProvider.cs @@ -3,12 +3,10 @@ using System.Runtime.InteropServices; using System.Text; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui.Toast; using Dalamud.Interface.Utility; using Dalamud.Logging.Internal; - using TerraFX.Interop.Windows; using static TerraFX.Interop.Windows.Windows; @@ -36,7 +34,7 @@ namespace Dalamud.Interface.Internal; [ServiceManager.EarlyLoadedService] internal sealed unsafe class ImGuiClipboardFunctionProvider : IInternalDisposableService { - private static readonly ModuleLog Log = ModuleLog.Create<ImGuiClipboardFunctionProvider>(); + private static readonly ModuleLog Log = new(nameof(ImGuiClipboardFunctionProvider)); private readonly void* clipboardUserDataOriginal; private readonly void* setTextOriginal; private readonly void* getTextOriginal; diff --git a/Dalamud/Interface/Internal/ImGuiInputTextStatePtrExtensions.cs b/Dalamud/Interface/Internal/ImGuiInputTextStatePtrExtensions.cs index e27c20d06..ab5fdaac8 100644 --- a/Dalamud/Interface/Internal/ImGuiInputTextStatePtrExtensions.cs +++ b/Dalamud/Interface/Internal/ImGuiInputTextStatePtrExtensions.cs @@ -110,7 +110,7 @@ internal static unsafe class ImGuiInputTextStatePtrExtensions var text = new Span<char>(self.TextW.Data, self.TextW.Size); if (pos != textLen) - text[pos..textLen].CopyTo(text[(pos + newText.Length)..]); + text.Slice(pos, textLen - pos).CopyTo(text[(pos + newText.Length)..]); newText.CopyTo(text[pos..]); self.Edited = true; diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index a1954524a..d68bc8bef 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -8,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Game; @@ -32,13 +32,10 @@ using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing.Persistence; using Dalamud.IoC.Internal; using Dalamud.Logging.Internal; -using Dalamud.Memory; using Dalamud.Plugin.Services; using Dalamud.Utility; using Dalamud.Utility.Timing; - using JetBrains.Annotations; - using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; @@ -79,10 +76,10 @@ internal partial class InterfaceManager : IInternalDisposableService /// </summary> public const float DefaultFontSizePx = (DefaultFontSizePt * 4.0f) / 3.0f; - private static readonly ModuleLog Log = ModuleLog.Create<InterfaceManager>(); + private static readonly ModuleLog Log = new("INTERFACE"); - private readonly ConcurrentBag<IDeferredDisposable> deferredDisposeTextures = []; - private readonly ConcurrentBag<IDisposable> deferredDisposeDisposables = []; + private readonly ConcurrentBag<IDeferredDisposable> deferredDisposeTextures = new(); + private readonly ConcurrentBag<IDisposable> deferredDisposeDisposables = new(); [ServiceManager.ServiceDependency] private readonly DalamudConfiguration dalamudConfiguration = Service<DalamudConfiguration>.Get(); @@ -259,7 +256,7 @@ internal partial class InterfaceManager : IInternalDisposableService var gwh = default(HWND); fixed (char* pClass = "FFXIVGAME") { - while ((gwh = FindWindowExW(default, gwh, pClass, default)) != default) + while ((gwh = FindWindowExW(default, gwh, (ushort*)pClass, default)) != default) { uint pid; _ = GetWindowThreadProcessId(gwh, &pid); @@ -503,34 +500,6 @@ internal partial class InterfaceManager : IInternalDisposableService ImGuiHelpers.ClearStacksOnContext(); } - /// <summary> - /// Applies immersive dark mode to the game window based on the current system theme setting. - /// </summary> - internal void SetImmersiveModeFromSystemTheme() - { - bool useDark = this.IsSystemInDarkMode(); - this.SetImmersiveMode(useDark); - } - - /// <summary> - /// Checks whether the system use dark mode. - /// </summary> - /// <returns>Returns true if dark mode is preferred.</returns> - internal bool IsSystemInDarkMode() - { - try - { - using var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey( - @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"); - var value = key?.GetValue("AppsUseLightTheme") as int?; - return value != 1; - } - catch - { - return false; - } - } - /// <summary> /// Toggle Windows 11 immersive mode on the game window. /// </summary> @@ -709,7 +678,8 @@ internal partial class InterfaceManager : IInternalDisposableService if (configuration.SavedStyles == null || configuration.SavedStyles.All(x => x.Name != StyleModelV1.DalamudStandard.Name)) { - configuration.SavedStyles = [StyleModelV1.DalamudStandard, StyleModelV1.DalamudClassic]; + configuration.SavedStyles = new List<StyleModel> + { StyleModelV1.DalamudStandard, StyleModelV1.DalamudClassic }; configuration.ChosenStyle = StyleModelV1.DalamudStandard.Name; } else if (configuration.SavedStyles.Count == 1) @@ -774,18 +744,6 @@ internal partial class InterfaceManager : IInternalDisposableService private void WndProcHookManagerOnPreWndProc(WndProcEventArgs args) { - if (args.Message == WM.WM_SETTINGCHANGE) - { - if (this.dalamudConfiguration.WindowIsImmersive) - { - if (MemoryHelper.EqualsZeroTerminatedWideString("ImmersiveColorSet", args.LParam) || - MemoryHelper.EqualsZeroTerminatedWideString("VisualStyleChanged", args.LParam)) - { - this.SetImmersiveModeFromSystemTheme(); - } - } - } - var r = this.backend?.ProcessWndProcW(args.Hwnd, args.Message, args.WParam, args.LParam); if (r is not null) args.SuppressWithValue(r.Value); @@ -900,7 +858,7 @@ internal partial class InterfaceManager : IInternalDisposableService { // Requires that game window to be there, which will be the case once game swap chain is initialized. if (Service<DalamudConfiguration>.Get().WindowIsImmersive) - this.SetImmersiveModeFromSystemTheme(); + this.SetImmersiveMode(true); } catch (Exception ex) { @@ -1217,7 +1175,6 @@ internal partial class InterfaceManager : IInternalDisposableService WindowSystem.HasAnyWindowSystemFocus = false; WindowSystem.FocusedWindowSystemNamespace = string.Empty; - WindowSystem.ShouldInhibitAtkCloseEvents = false; if (this.IsDispatchingEvents) { diff --git a/Dalamud/Interface/Internal/PluginCategoryManager.cs b/Dalamud/Interface/Internal/PluginCategoryManager.cs index 2b7f0f354..d3aea7f57 100644 --- a/Dalamud/Interface/Internal/PluginCategoryManager.cs +++ b/Dalamud/Interface/Internal/PluginCategoryManager.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using CheapLoc; - using Dalamud.Plugin.Internal; using Dalamud.Plugin.Internal.Types; @@ -59,8 +58,8 @@ internal class PluginCategoryManager private CategoryKind currentCategoryKind = CategoryKind.All; private bool isContentDirty; - private Dictionary<PluginManifest, CategoryKind[]> mapPluginCategories = []; - private List<CategoryKind> highlightedCategoryKinds = []; + private Dictionary<PluginManifest, CategoryKind[]> mapPluginCategories = new(); + private List<CategoryKind> highlightedCategoryKinds = new(); /// <summary> /// Type of category group. @@ -514,7 +513,8 @@ internal class PluginCategoryManager this.GroupKind = groupKind; this.nameFunc = nameFunc; - this.Categories = [.. categories]; + this.Categories = new(); + this.Categories.AddRange(categories); } /// <summary> diff --git a/Dalamud/Interface/Internal/ReShadeHandling/ReShadeAddonInterface.Exports.cs b/Dalamud/Interface/Internal/ReShadeHandling/ReShadeAddonInterface.Exports.cs index d7d3b56c3..d8d210076 100644 --- a/Dalamud/Interface/Internal/ReShadeHandling/ReShadeAddonInterface.Exports.cs +++ b/Dalamud/Interface/Internal/ReShadeHandling/ReShadeAddonInterface.Exports.cs @@ -63,11 +63,11 @@ internal sealed unsafe partial class ReShadeAddonInterface return; - static bool GetProcAddressInto(ProcessModule m, ReadOnlySpan<char> name, void* res) + bool GetProcAddressInto(ProcessModule m, ReadOnlySpan<char> name, void* res) { Span<byte> name8 = stackalloc byte[Encoding.UTF8.GetByteCount(name) + 1]; name8[Encoding.UTF8.GetBytes(name, name8)] = 0; - *(nint*)res = (nint)GetProcAddress((HMODULE)m.BaseAddress, (sbyte*)Unsafe.AsPointer(ref name8[0])); + *(nint*)res = GetProcAddress((HMODULE)m.BaseAddress, (sbyte*)Unsafe.AsPointer(ref name8[0])); return *(nint*)res != 0; } } @@ -174,7 +174,7 @@ internal sealed unsafe partial class ReShadeAddonInterface CERT.CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT.CERT_NAME_ISSUER_FLAG, null, - (char*)Unsafe.AsPointer(ref issuerName[0]), + (ushort*)Unsafe.AsPointer(ref issuerName[0]), pcb); if (pcb == 0) throw new Win32Exception("CertGetNameStringW(2)"); diff --git a/Dalamud/Interface/Internal/ReShadeHandling/ReShadeUnwrapper.cs b/Dalamud/Interface/Internal/ReShadeHandling/ReShadeUnwrapper.cs index 711de6eb2..f1210425d 100644 --- a/Dalamud/Interface/Internal/ReShadeHandling/ReShadeUnwrapper.cs +++ b/Dalamud/Interface/Internal/ReShadeHandling/ReShadeUnwrapper.cs @@ -94,7 +94,7 @@ internal static unsafe class ReShadeUnwrapper static bool HasProcExported(ProcessModule m, ReadOnlySpan<byte> name) { fixed (byte* p = name) - return GetProcAddress((HMODULE)m.BaseAddress, (sbyte*)p) != null; + return GetProcAddress((HMODULE)m.BaseAddress, (sbyte*)p) != 0; } } diff --git a/Dalamud/Interface/Internal/StaThreadService.cs b/Dalamud/Interface/Internal/StaThreadService.cs index 5e93bbf75..87e003288 100644 --- a/Dalamud/Interface/Internal/StaThreadService.cs +++ b/Dalamud/Interface/Internal/StaThreadService.cs @@ -113,7 +113,7 @@ internal partial class StaThreadService : IInternalDisposableService using var cts = CancellationTokenSource.CreateLinkedTokenSource( this.cancellationTokenSource.Token, cancellationToken); - await this.taskFactory.StartNew(action, cts.Token).ConfigureAwait(true); + await this.taskFactory.StartNew(action, cancellationToken).ConfigureAwait(true); } /// <summary>Runs a given delegate in the messaging thread.</summary> @@ -126,7 +126,7 @@ internal partial class StaThreadService : IInternalDisposableService using var cts = CancellationTokenSource.CreateLinkedTokenSource( this.cancellationTokenSource.Token, cancellationToken); - return await this.taskFactory.StartNew(func, cts.Token).ConfigureAwait(true); + return await this.taskFactory.StartNew(func, cancellationToken).ConfigureAwait(true); } /// <summary>Runs a given delegate in the messaging thread.</summary> @@ -138,7 +138,7 @@ internal partial class StaThreadService : IInternalDisposableService using var cts = CancellationTokenSource.CreateLinkedTokenSource( this.cancellationTokenSource.Token, cancellationToken); - await await this.taskFactory.StartNew(func, cts.Token).ConfigureAwait(true); + await await this.taskFactory.StartNew(func, cancellationToken).ConfigureAwait(true); } /// <summary>Runs a given delegate in the messaging thread.</summary> @@ -151,7 +151,7 @@ internal partial class StaThreadService : IInternalDisposableService using var cts = CancellationTokenSource.CreateLinkedTokenSource( this.cancellationTokenSource.Token, cancellationToken); - return await await this.taskFactory.StartNew(func, cts.Token).ConfigureAwait(true); + return await await this.taskFactory.StartNew(func, cancellationToken).ConfigureAwait(true); } [LibraryImport("ole32.dll")] @@ -216,7 +216,7 @@ internal partial class StaThreadService : IInternalDisposableService lpfnWndProc = &MessageReceiverWndProcStatic, hInstance = hInstance, hbrBackground = (HBRUSH)(COLOR.COLOR_BACKGROUND + 1), - lpszClassName = name, + lpszClassName = (ushort*)name, }; wndClassAtom = RegisterClassExW(&wndClass); @@ -226,8 +226,8 @@ internal partial class StaThreadService : IInternalDisposableService this.messageReceiverHwndTask.SetResult( CreateWindowExW( 0, - (char*)wndClassAtom, - name, + (ushort*)wndClassAtom, + (ushort*)name, 0, CW_USEDEFAULT, CW_USEDEFAULT, @@ -275,7 +275,7 @@ internal partial class StaThreadService : IInternalDisposableService _ = OleFlushClipboard(); OleUninitialize(); if (wndClassAtom != 0) - UnregisterClassW((char*)wndClassAtom, hInstance); + UnregisterClassW((ushort*)wndClassAtom, hInstance); this.messageReceiverHwndTask.TrySetException(e); } } diff --git a/Dalamud/Interface/Internal/UiDebug.cs b/Dalamud/Interface/Internal/UiDebug.cs new file mode 100644 index 000000000..1211b505d --- /dev/null +++ b/Dalamud/Interface/Internal/UiDebug.cs @@ -0,0 +1,676 @@ +using System.Numerics; + +using Dalamud.Bindings.ImGui; +using Dalamud.Game; +using Dalamud.Game.Gui; +using Dalamud.Interface.ImGuiSeStringRenderer.Internal; +using Dalamud.Interface.Textures.Internal; +using Dalamud.Interface.Utility; +using Dalamud.Utility; + +using FFXIVClientStructs.FFXIV.Client.System.String; +using FFXIVClientStructs.FFXIV.Client.UI.Misc; +using FFXIVClientStructs.FFXIV.Component.GUI; +using Lumina.Text.ReadOnly; + +// Customised version of https://github.com/aers/FFXIVUIDebug + +namespace Dalamud.Interface.Internal; + +/// <summary> +/// This class displays a debug window to inspect native addons. +/// </summary> +internal unsafe class UiDebug +{ + private const int UnitListCount = 18; + + private readonly bool[] selectedInList = new bool[UnitListCount]; + private readonly string[] listNames = new string[UnitListCount] + { + "Depth Layer 1", + "Depth Layer 2", + "Depth Layer 3", + "Depth Layer 4", + "Depth Layer 5", + "Depth Layer 6", + "Depth Layer 7", + "Depth Layer 8", + "Depth Layer 9", + "Depth Layer 10", + "Depth Layer 11", + "Depth Layer 12", + "Depth Layer 13", + "Loaded Units", + "Focused Units", + "Units 16", + "Units 17", + "Units 18", + }; + + private bool doingSearch; + private string searchInput = string.Empty; + private AtkUnitBase* selectedUnitBase = null; + + /// <summary> + /// Initializes a new instance of the <see cref="UiDebug"/> class. + /// </summary> + public UiDebug() + { + } + + /// <summary> + /// Renders this window. + /// </summary> + public void Draw() + { + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(3, 2)); + ImGui.BeginChild("st_uiDebug_unitBaseSelect"u8, new Vector2(250, -1), true); + + ImGui.SetNextItemWidth(-1); + ImGui.InputTextWithHint("###atkUnitBaseSearch"u8, "Search"u8, ref this.searchInput, 0x20); + + this.DrawUnitBaseList(); + ImGui.EndChild(); + if (this.selectedUnitBase != null) + { + ImGui.SameLine(); + ImGui.BeginChild("st_uiDebug_selectedUnitBase"u8, new Vector2(-1, -1), true); + this.DrawUnitBase(this.selectedUnitBase); + ImGui.EndChild(); + } + + ImGui.PopStyleVar(); + } + + private void DrawUnitBase(AtkUnitBase* atkUnitBase) + { + var isVisible = atkUnitBase->IsVisible; + var addonName = atkUnitBase->NameString; + var agent = Service<GameGui>.Get().FindAgentInterface(atkUnitBase); + + ImGui.Text(addonName); + ImGui.SameLine(); + ImGui.PushStyleColor(ImGuiCol.Text, isVisible ? 0xFF00FF00 : 0xFF0000FF); + ImGui.Text(isVisible ? "Visible" : "Not Visible"); + ImGui.PopStyleColor(); + + ImGui.SameLine(ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X - 25); + if (ImGui.SmallButton("V"u8)) + { + atkUnitBase->IsVisible = !atkUnitBase->IsVisible; + } + + ImGui.Separator(); + ImGuiHelpers.ClickToCopyText($"Address: {(nint)atkUnitBase:X}", $"{(nint)atkUnitBase:X}"); + ImGuiHelpers.ClickToCopyText($"Agent: {(nint)agent:X}", $"{(nint)agent:X}"); + ImGui.Separator(); + + ImGui.Text($"Position: [ {atkUnitBase->X} , {atkUnitBase->Y} ]"); + ImGui.Text($"Scale: {atkUnitBase->Scale * 100}%"); + ImGui.Text($"Widget Count {atkUnitBase->UldManager.ObjectCount}"); + + ImGui.Separator(); + + object addonObj = *atkUnitBase; + + Util.ShowStruct(addonObj, (ulong)atkUnitBase); + + ImGui.Dummy(new Vector2(25 * ImGui.GetIO().FontGlobalScale)); + ImGui.Separator(); + if (atkUnitBase->RootNode != null) + this.PrintNode(atkUnitBase->RootNode); + + if (atkUnitBase->UldManager.NodeListCount > 0) + { + ImGui.Dummy(new Vector2(25 * ImGui.GetIO().FontGlobalScale)); + ImGui.Separator(); + ImGui.PushStyleColor(ImGuiCol.Text, 0xFFFFAAAA); + if (ImGui.TreeNode($"Node List##{(ulong)atkUnitBase:X}")) + { + ImGui.PopStyleColor(); + + for (var j = 0; j < atkUnitBase->UldManager.NodeListCount; j++) + { + this.PrintNode(atkUnitBase->UldManager.NodeList[j], false, $"[{j}] "); + } + + ImGui.TreePop(); + } + else + { + ImGui.PopStyleColor(); + } + } + } + + private void PrintNode(AtkResNode* node, bool printSiblings = true, string treePrefix = "") + { + if (node == null) + return; + + if ((int)node->Type < 1000) + this.PrintSimpleNode(node, treePrefix); + else + this.PrintComponentNode(node, treePrefix); + + if (printSiblings) + { + var prevNode = node; + while ((prevNode = prevNode->PrevSiblingNode) != null) + this.PrintNode(prevNode, false, "prev "); + + var nextNode = node; + while ((nextNode = nextNode->NextSiblingNode) != null) + this.PrintNode(nextNode, false, "next "); + } + } + + private void PrintSimpleNode(AtkResNode* node, string treePrefix) + { + var popped = false; + var isVisible = node->NodeFlags.HasFlag(NodeFlags.Visible); + + if (isVisible) + ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(0, 255, 0, 255)); + + if (ImGui.TreeNode($"{treePrefix}{node->Type} Node (ptr = {(long)node:X})###{(long)node}")) + { + if (ImGui.IsItemHovered()) + this.DrawOutline(node); + + if (isVisible) + { + ImGui.PopStyleColor(); + popped = true; + } + + ImGui.Text("Node: "u8); + ImGui.SameLine(); + ImGuiHelpers.ClickToCopyText($"{(ulong)node:X}"); + ImGui.SameLine(); + switch (node->Type) + { + case NodeType.Text: Util.ShowStruct(*(AtkTextNode*)node, (ulong)node); break; + case NodeType.Image: Util.ShowStruct(*(AtkImageNode*)node, (ulong)node); break; + case NodeType.Collision: Util.ShowStruct(*(AtkCollisionNode*)node, (ulong)node); break; + case NodeType.NineGrid: Util.ShowStruct(*(AtkNineGridNode*)node, (ulong)node); break; + case NodeType.ClippingMask: Util.ShowStruct(*(AtkClippingMaskNode*)node, (ulong)node); break; + case NodeType.Counter: Util.ShowStruct(*(AtkCounterNode*)node, (ulong)node); break; + default: Util.ShowStruct(*node, (ulong)node); break; + } + + this.PrintResNode(node); + + if (node->ChildNode != null) + this.PrintNode(node->ChildNode); + + switch (node->Type) + { + case NodeType.Text: + var textNode = (AtkTextNode*)node; + ImGui.Text("text: "u8); + ImGui.SameLine(); + Service<SeStringRenderer>.Get().Draw(textNode->NodeText); + + ImGui.InputText($"Replace Text##{(ulong)textNode:X}", new(textNode->NodeText.StringPtr, (int)textNode->NodeText.BufSize)); + + ImGui.SameLine(); + if (ImGui.Button($"Encode##{(ulong)textNode:X}")) + { + using var tmp = new Utf8String(); + RaptureTextModule.Instance()->MacroEncoder.EncodeString(&tmp, textNode->NodeText.StringPtr); + textNode->NodeText.Copy(&tmp); + } + + ImGui.SameLine(); + if (ImGui.Button($"Decode##{(ulong)textNode:X}")) + textNode->NodeText.SetString(textNode->NodeText.StringPtr.AsReadOnlySeStringSpan().ToString()); + + ImGui.Text($"AlignmentType: {(AlignmentType)textNode->AlignmentFontType} FontSize: {textNode->FontSize}"); + int b = textNode->AlignmentFontType; + if (ImGui.InputInt($"###setAlignment{(ulong)textNode:X}", ref b, 1)) + { + while (b > byte.MaxValue) b -= byte.MaxValue; + while (b < byte.MinValue) b += byte.MaxValue; + textNode->AlignmentFontType = (byte)b; + textNode->AtkResNode.DrawFlags |= 0x1; + } + + ImGui.Text($"Color: #{textNode->TextColor.R:X2}{textNode->TextColor.G:X2}{textNode->TextColor.B:X2}{textNode->TextColor.A:X2}"); + ImGui.SameLine(); + ImGui.Text($"EdgeColor: #{textNode->EdgeColor.R:X2}{textNode->EdgeColor.G:X2}{textNode->EdgeColor.B:X2}{textNode->EdgeColor.A:X2}"); + ImGui.SameLine(); + ImGui.Text($"BGColor: #{textNode->BackgroundColor.R:X2}{textNode->BackgroundColor.G:X2}{textNode->BackgroundColor.B:X2}{textNode->BackgroundColor.A:X2}"); + + ImGui.Text($"TextFlags: {textNode->TextFlags}"); + + break; + case NodeType.Counter: + var counterNode = (AtkCounterNode*)node; + ImGui.Text("text: "u8); + ImGui.SameLine(); + Service<SeStringRenderer>.Get().Draw(counterNode->NodeText); + break; + case NodeType.Image: + var imageNode = (AtkImageNode*)node; + PrintTextureInfo(imageNode->PartsList, imageNode->PartId); + break; + case NodeType.NineGrid: + var ngNode = (AtkNineGridNode*)node; + PrintTextureInfo(ngNode->PartsList, ngNode->PartId); + break; + case NodeType.ClippingMask: + var cmNode = (AtkClippingMaskNode*)node; + PrintTextureInfo(cmNode->PartsList, cmNode->PartId); + break; + } + + ImGui.TreePop(); + } + else if (ImGui.IsItemHovered()) + { + this.DrawOutline(node); + } + + if (isVisible && !popped) + ImGui.PopStyleColor(); + + static void PrintTextureInfo(AtkUldPartsList* partsList, uint partId) + { + if (partsList != null) + { + if (partId > partsList->PartCount) + { + ImGui.Text("part id > part count?"u8); + } + else + { + var textureInfo = partsList->Parts[partId].UldAsset; + var texType = textureInfo->AtkTexture.TextureType; + ImGui.Text( + $"texture type: {texType} part_id={partId} part_id_count={partsList->PartCount}"); + if (texType == TextureType.Resource) + { + ImGui.Text( + $"texture path: {textureInfo->AtkTexture.Resource->TexFileResourceHandle->ResourceHandle.FileName}"); + var kernelTexture = textureInfo->AtkTexture.Resource->KernelTextureObject; + + if (ImGui.TreeNode($"Texture##{(ulong)kernelTexture->D3D11ShaderResourceView:X}")) + { + ImGui.Image( + new ImTextureID(kernelTexture->D3D11ShaderResourceView), + new Vector2(kernelTexture->ActualWidth, kernelTexture->ActualHeight)); + ImGui.TreePop(); + } + } + else if (texType == TextureType.KernelTexture) + { + if (ImGui.TreeNode( + $"Texture##{(ulong)textureInfo->AtkTexture.KernelTexture->D3D11ShaderResourceView:X}")) + { + ImGui.Image( + new ImTextureID(textureInfo->AtkTexture.KernelTexture->D3D11ShaderResourceView), + new Vector2( + textureInfo->AtkTexture.KernelTexture->ActualWidth, + textureInfo->AtkTexture.KernelTexture->ActualHeight)); + ImGui.TreePop(); + } + } + + if (ImGui.Button($"Replace with a random image##{(ulong)textureInfo:X}")) + { + var texm = Service<TextureManager>.Get(); + texm.Shared + .GetFromGame( + Random.Shared.Next(0, 1) == 0 + ? $"ui/loadingimage/-nowloading_base{Random.Shared.Next(1, 33)}.tex" + : $"ui/loadingimage/-nowloading_base{Random.Shared.Next(1, 33)}_hr1.tex") + .RentAsync() + .ContinueWith( + r => Service<Framework>.Get().RunOnFrameworkThread( + () => + { + if (!r.IsCompletedSuccessfully) + return; + + using (r.Result) + { + textureInfo->AtkTexture.ReleaseTexture(); + textureInfo->AtkTexture.KernelTexture = + texm.ConvertToKernelTexture(r.Result); + textureInfo->AtkTexture.TextureType = TextureType.KernelTexture; + } + })); + } + } + } + else + { + ImGui.Text("no texture loaded"u8); + } + } + } + + private void PrintComponentNode(AtkResNode* node, string treePrefix) + { + var compNode = (AtkComponentNode*)node; + + var popped = false; + var isVisible = node->NodeFlags.HasFlag(NodeFlags.Visible); + + var componentInfo = compNode->Component->UldManager; + + var childCount = componentInfo.NodeListCount; + + var objectInfo = (AtkUldComponentInfo*)componentInfo.Objects; + if (objectInfo == null) + { + return; + } + + if (isVisible) + ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(0, 255, 0, 255)); + + if (ImGui.TreeNode($"{treePrefix}{objectInfo->ComponentType} Component Node (ptr = {(long)node:X}, component ptr = {(long)compNode->Component:X}) child count = {childCount} ###{(long)node}")) + { + if (ImGui.IsItemHovered()) + this.DrawOutline(node); + + if (isVisible) + { + ImGui.PopStyleColor(); + popped = true; + } + + ImGui.Text("Node: "u8); + ImGui.SameLine(); + ImGuiHelpers.ClickToCopyText($"{(ulong)node:X}"); + ImGui.SameLine(); + Util.ShowStruct(*compNode, (ulong)compNode); + ImGui.Text("Component: "u8); + ImGui.SameLine(); + ImGuiHelpers.ClickToCopyText($"{(ulong)compNode->Component:X}"); + ImGui.SameLine(); + + switch (objectInfo->ComponentType) + { + case ComponentType.Button: Util.ShowStruct(*(AtkComponentButton*)compNode->Component, (ulong)compNode->Component); break; + case ComponentType.Slider: Util.ShowStruct(*(AtkComponentSlider*)compNode->Component, (ulong)compNode->Component); break; + case ComponentType.Window: Util.ShowStruct(*(AtkComponentWindow*)compNode->Component, (ulong)compNode->Component); break; + case ComponentType.CheckBox: Util.ShowStruct(*(AtkComponentCheckBox*)compNode->Component, (ulong)compNode->Component); break; + case ComponentType.GaugeBar: Util.ShowStruct(*(AtkComponentGaugeBar*)compNode->Component, (ulong)compNode->Component); break; + case ComponentType.RadioButton: Util.ShowStruct(*(AtkComponentRadioButton*)compNode->Component, (ulong)compNode->Component); break; + case ComponentType.TextInput: Util.ShowStruct(*(AtkComponentTextInput*)compNode->Component, (ulong)compNode->Component); break; + case ComponentType.Icon: Util.ShowStruct(*(AtkComponentIcon*)compNode->Component, (ulong)compNode->Component); break; + default: Util.ShowStruct(*compNode->Component, (ulong)compNode->Component); break; + } + + this.PrintResNode(node); + this.PrintNode(componentInfo.RootNode); + + switch (objectInfo->ComponentType) + { + case ComponentType.TextInput: + var textInputComponent = (AtkComponentTextInput*)compNode->Component; + ImGui.Text("InputBase Text1: "u8); + ImGui.SameLine(); + Service<SeStringRenderer>.Get().Draw(textInputComponent->AtkComponentInputBase.EvaluatedString); + + ImGui.Text("InputBase Text2: "u8); + ImGui.SameLine(); + Service<SeStringRenderer>.Get().Draw(textInputComponent->AtkComponentInputBase.RawString); + + ImGui.Text("Text1: "u8); + ImGui.SameLine(); + Service<SeStringRenderer>.Get().Draw(textInputComponent->UnkText01); + + ImGui.Text("Text2: "u8); + ImGui.SameLine(); + Service<SeStringRenderer>.Get().Draw(textInputComponent->UnkText02); + + ImGui.Text("AvailableLines: "u8); + ImGui.SameLine(); + Service<SeStringRenderer>.Get().Draw(textInputComponent->AvailableLines); + + ImGui.Text("HighlightedAutoTranslateOptionColorPrefix: "u8); + ImGui.SameLine(); + Service<SeStringRenderer>.Get().Draw(textInputComponent->HighlightedAutoTranslateOptionColorPrefix); + + ImGui.Text("HighlightedAutoTranslateOptionColorSuffix: "u8); + ImGui.SameLine(); + Service<SeStringRenderer>.Get().Draw(textInputComponent->HighlightedAutoTranslateOptionColorSuffix); + break; + } + + ImGui.PushStyleColor(ImGuiCol.Text, 0xFFFFAAAA); + if (ImGui.TreeNode($"Node List##{(ulong)node:X}")) + { + ImGui.PopStyleColor(); + + for (var i = 0; i < compNode->Component->UldManager.NodeListCount; i++) + { + this.PrintNode(compNode->Component->UldManager.NodeList[i], false, $"[{i}] "); + } + + ImGui.TreePop(); + } + else + { + ImGui.PopStyleColor(); + } + + ImGui.TreePop(); + } + else if (ImGui.IsItemHovered()) + { + this.DrawOutline(node); + } + + if (isVisible && !popped) + ImGui.PopStyleColor(); + } + + private void PrintResNode(AtkResNode* node) + { + ImGui.Text($"NodeID: {node->NodeId}"); + ImGui.SameLine(); + if (ImGui.SmallButton($"T:Visible##{(ulong)node:X}")) + { + node->NodeFlags ^= NodeFlags.Visible; + } + + ImGui.SameLine(); + if (ImGui.SmallButton($"C:Ptr##{(ulong)node:X}")) + { + ImGui.SetClipboardText($"{(ulong)node:X}"); + } + + ImGui.Text( + $"X: {node->X} Y: {node->Y} " + + $"ScaleX: {node->ScaleX} ScaleY: {node->ScaleY} " + + $"Rotation: {node->Rotation} " + + $"Width: {node->Width} Height: {node->Height} " + + $"OriginX: {node->OriginX} OriginY: {node->OriginY}"); + ImGui.Text( + $"RGBA: 0x{node->Color.R:X2}{node->Color.G:X2}{node->Color.B:X2}{node->Color.A:X2} " + + $"AddRGB: {node->AddRed} {node->AddGreen} {node->AddBlue} " + + $"MultiplyRGB: {node->MultiplyRed} {node->MultiplyGreen} {node->MultiplyBlue}"); + } + + private bool DrawUnitListHeader(int index, ushort count, ulong ptr, bool highlight) + { + ImGui.PushStyleColor(ImGuiCol.Text, highlight ? 0xFFAAAA00 : 0xFFFFFFFF); + if (!string.IsNullOrEmpty(this.searchInput) && !this.doingSearch) + { + ImGui.SetNextItemOpen(true, ImGuiCond.Always); + } + else if (this.doingSearch && string.IsNullOrEmpty(this.searchInput)) + { + ImGui.SetNextItemOpen(false, ImGuiCond.Always); + } + + var treeNode = ImGui.TreeNode($"{this.listNames[index]}##unitList_{index}"); + ImGui.PopStyleColor(); + + ImGui.SameLine(); + ImGui.TextDisabled($"C:{count} {ptr:X}"); + return treeNode; + } + + private void DrawUnitBaseList() + { + var foundSelected = false; + var noResults = true; + var stage = AtkStage.Instance(); + + var unitManagers = &stage->RaptureAtkUnitManager->AtkUnitManager.DepthLayerOneList; + + var searchStr = this.searchInput; + var searching = !string.IsNullOrEmpty(searchStr); + + for (var i = 0; i < UnitListCount; i++) + { + var headerDrawn = false; + + var highlight = this.selectedUnitBase != null && this.selectedInList[i]; + this.selectedInList[i] = false; + var unitManager = &unitManagers[i]; + + var headerOpen = true; + + if (!searching) + { + headerOpen = this.DrawUnitListHeader(i, unitManager->Count, (ulong)unitManager, highlight); + headerDrawn = true; + noResults = false; + } + + for (var j = 0; j < unitManager->Count && headerOpen; j++) + { + AtkUnitBase* unitBase = unitManager->Entries[j]; + if (this.selectedUnitBase != null && unitBase == this.selectedUnitBase) + { + this.selectedInList[i] = true; + foundSelected = true; + } + + var name = unitBase->NameString; + if (searching) + { + if (name == null || !name.ToLowerInvariant().Contains(searchStr.ToLowerInvariant())) continue; + } + + noResults = false; + if (!headerDrawn) + { + headerOpen = this.DrawUnitListHeader(i, unitManager->Count, (ulong)unitManager, highlight); + headerDrawn = true; + } + + if (headerOpen) + { + var visible = unitBase->IsVisible; + ImGui.PushStyleColor(ImGuiCol.Text, visible ? 0xFF00FF00 : 0xFF999999); + + if (ImGui.Selectable($"{name}##list{i}-{(ulong)unitBase:X}_{j}", this.selectedUnitBase == unitBase)) + { + this.selectedUnitBase = unitBase; + foundSelected = true; + this.selectedInList[i] = true; + } + + ImGui.PopStyleColor(); + } + } + + if (headerDrawn && headerOpen) + { + ImGui.TreePop(); + } + + if (this.selectedInList[i] == false && this.selectedUnitBase != null) + { + for (var j = 0; j < unitManager->Count; j++) + { + AtkUnitBase* unitBase = unitManager->Entries[j]; + if (this.selectedUnitBase == null || unitBase != this.selectedUnitBase) continue; + this.selectedInList[i] = true; + foundSelected = true; + } + } + } + + if (noResults) + { + ImGui.TextDisabled("No Results"u8); + } + + if (!foundSelected) + { + this.selectedUnitBase = null; + } + + if (this.doingSearch && string.IsNullOrEmpty(this.searchInput)) + { + this.doingSearch = false; + } + else if (!this.doingSearch && !string.IsNullOrEmpty(this.searchInput)) + { + this.doingSearch = true; + } + } + + private Vector2 GetNodePosition(AtkResNode* node) + { + var pos = new Vector2(node->X, node->Y); + pos -= new Vector2(node->OriginX * (node->ScaleX - 1), node->OriginY * (node->ScaleY - 1)); + var par = node->ParentNode; + while (par != null) + { + pos *= new Vector2(par->ScaleX, par->ScaleY); + pos += new Vector2(par->X, par->Y); + pos -= new Vector2(par->OriginX * (par->ScaleX - 1), par->OriginY * (par->ScaleY - 1)); + par = par->ParentNode; + } + + return pos; + } + + private Vector2 GetNodeScale(AtkResNode* node) + { + if (node == null) return new Vector2(1, 1); + var scale = new Vector2(node->ScaleX, node->ScaleY); + while (node->ParentNode != null) + { + node = node->ParentNode; + scale *= new Vector2(node->ScaleX, node->ScaleY); + } + + return scale; + } + + private bool GetNodeVisible(AtkResNode* node) + { + if (node == null) return false; + while (node != null) + { + if (!node->NodeFlags.HasFlag(NodeFlags.Visible)) return false; + node = node->ParentNode; + } + + return true; + } + + private void DrawOutline(AtkResNode* node) + { + var position = this.GetNodePosition(node); + var scale = this.GetNodeScale(node); + var size = new Vector2(node->Width, node->Height) * scale; + + var nodeVisible = this.GetNodeVisible(node); + + position += ImGuiHelpers.MainViewport.Pos; + + ImGui.GetForegroundDrawList(ImGuiHelpers.MainViewport).AddRect(position, position + size, nodeVisible ? 0xFF00FF00 : 0xFF0000FF); + } +} diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/AddonTree.AtkValues.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.AtkValues.cs similarity index 96% rename from Dalamud/Interface/Internal/UiDebug/Browsing/AddonTree.AtkValues.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.AtkValues.cs index 646d4e3ad..b31f74264 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/AddonTree.AtkValues.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.AtkValues.cs @@ -1,15 +1,16 @@ using System.Numerics; using Dalamud.Bindings.ImGui; -using Dalamud.Interface.Internal.UiDebug.Utility; +using Dalamud.Interface.Internal.UiDebug2.Utility; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; +using Dalamud.Memory; using Dalamud.Utility; - using FFXIVClientStructs.FFXIV.Component.GUI; using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType; -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <inheritdoc cref="AddonTree"/> public unsafe partial class AddonTree @@ -27,6 +28,7 @@ public unsafe partial class AddonTree if (tree.Success) { using var tbl = ImRaii.Table("atkUnitBase_atkValueTable"u8, 3, ImGuiTableFlags.Borders | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg); + if (tbl.Success) { ImGui.TableSetupColumn("Index"u8); diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/AddonTree.FieldNames.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs similarity index 95% rename from Dalamud/Interface/Internal/UiDebug/Browsing/AddonTree.FieldNames.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs index 3470b724b..0b1dcb66c 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/AddonTree.FieldNames.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs @@ -7,9 +7,9 @@ using FFXIVClientStructs.Attributes; using FFXIVClientStructs.FFXIV.Component.GUI; using static System.Reflection.BindingFlags; -using static Dalamud.Interface.Internal.UiDebug.UiDebug; +using static Dalamud.Interface.Internal.UiDebug2.UiDebug2; -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <inheritdoc cref="AddonTree"/> public unsafe partial class AddonTree @@ -41,7 +41,7 @@ public unsafe partial class AddonTree { foreach (var t in from t in ClientStructsAssembly.GetTypes() where t.IsPublic - let xivAddonAttr = t.GetCustomAttribute<AddonAttribute>(false) + let xivAddonAttr = (AddonAttribute?)t.GetCustomAttribute(typeof(AddonAttribute), false) where xivAddonAttr != null where xivAddonAttr.AddonIdentifiers.Contains(this.AddonName) select t) @@ -83,7 +83,7 @@ public unsafe partial class AddonTree foreach (var field in baseType.GetFields(Static | Public | NonPublic | Instance)) { - if (field.GetCustomAttribute<FieldOffsetAttribute>() is FieldOffsetAttribute offset) + if (field.GetCustomAttribute(typeof(FieldOffsetAttribute)) is FieldOffsetAttribute offset) { try { diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/AddonTree.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs similarity index 96% rename from Dalamud/Interface/Internal/UiDebug/Browsing/AddonTree.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs index 954755708..7cb2cc704 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/AddonTree.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs @@ -4,16 +4,17 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Components; +using Dalamud.Interface.Utility; using FFXIVClientStructs.FFXIV.Component.GUI; using static Dalamud.Interface.FontAwesomeIcon; -using static Dalamud.Interface.Internal.UiDebug.ElementSelector; -using static Dalamud.Interface.Internal.UiDebug.UiDebug; -using static Dalamud.Interface.Internal.UiDebug.Utility.Gui; +using static Dalamud.Interface.Internal.UiDebug2.ElementSelector; +using static Dalamud.Interface.Internal.UiDebug2.UiDebug2; +using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui; using static Dalamud.Utility.Util; -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <summary> /// A class representing an <see cref="AtkUnitBase"/>, allowing it to be browsed within an ImGui window. diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/Events.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs similarity index 97% rename from Dalamud/Interface/Internal/UiDebug/Browsing/Events.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs index 6e56c75f8..98c7d9efe 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/Events.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs @@ -3,13 +3,12 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; - using FFXIVClientStructs.FFXIV.Component.GUI; using static Dalamud.Bindings.ImGui.ImGuiTableColumnFlags; using static Dalamud.Bindings.ImGui.ImGuiTableFlags; -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <summary> /// Class that prints the events table for a node, where applicable. @@ -29,9 +28,11 @@ public static class Events } using var tree = ImRaii.TreeNode($"Events##{(nint)node:X}eventTree"); + if (tree.Success) { using var tbl = ImRaii.Table($"##{(nint)node:X}eventTable", 7, Resizable | SizingFixedFit | Borders | RowBg); + if (tbl.Success) { ImGui.TableSetupColumn("#"u8, WidthFixed); @@ -49,25 +50,18 @@ public static class Events { ImGui.TableNextColumn(); ImGui.Text($"{i++}"); - ImGui.TableNextColumn(); ImGui.Text($"{evt->State.EventType}"); - ImGui.TableNextColumn(); ImGui.Text($"{evt->Param}"); - ImGui.TableNextColumn(); ImGui.Text($"{evt->State.StateFlags}"); - ImGui.TableNextColumn(); ImGui.Text($"{evt->State.ReturnFlags}"); - ImGui.TableNextColumn(); ImGuiHelpers.ClickToCopyText($"{(nint)evt->Target:X}", default, new Vector4(0.6f, 0.6f, 0.6f, 1)); - ImGui.TableNextColumn(); ImGuiHelpers.ClickToCopyText($"{(nint)evt->Listener:X}", default, new Vector4(0.6f, 0.6f, 0.6f, 1)); - evt = evt->NextEvent; } } diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.ClippingMask.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.ClippingMask.cs similarity index 95% rename from Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.ClippingMask.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.ClippingMask.cs index f9fb3b73d..cfba1a2bc 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.ClippingMask.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.ClippingMask.cs @@ -2,7 +2,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI; using static Dalamud.Utility.Util; -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <summary> /// A tree for an <see cref="AtkClippingMaskNode"/> that can be printed and browsed via ImGui. diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Collision.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Collision.cs similarity index 93% rename from Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Collision.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Collision.cs index d6370d33f..c447afac9 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Collision.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Collision.cs @@ -2,7 +2,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI; using static Dalamud.Utility.Util; -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <summary> /// A tree for an <see cref="AtkCollisionNode"/> that can be printed and browsed via ImGui. diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Component.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs similarity index 89% rename from Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Component.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs index fb4444be1..a35195498 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Component.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs @@ -1,16 +1,13 @@ using System.Runtime.InteropServices; using Dalamud.Bindings.ImGui; - using FFXIVClientStructs.FFXIV.Component.GUI; -using Lumina.Text.ReadOnly; - -using static Dalamud.Interface.Internal.UiDebug.Utility.Gui; +using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui; using static Dalamud.Utility.Util; using static FFXIVClientStructs.FFXIV.Component.GUI.ComponentType; -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <summary> /// A tree for an <see cref="AtkComponentNode"/> that can be printed and browsed via ImGui. @@ -92,14 +89,20 @@ internal unsafe class ComponentNodeTree : ResNodeTree { case TextInput: var textInputComponent = (AtkComponentTextInput*)this.Component; - ImGui.Text($"InputBase Text1 (Lumina): {new ReadOnlySeStringSpan(textInputComponent->AtkComponentInputBase.EvaluatedString.AsSpan()).ToMacroString()}"); - ImGui.Text($"InputBase Text2 (Lumina): {new ReadOnlySeStringSpan(textInputComponent->AtkComponentInputBase.RawString.AsSpan()).ToMacroString()}"); - // TODO: Reenable when unknowns have been unprivated / named - // ImGui.Text($"Text1: {new ReadOnlySeStringSpan(textInputComponent->UnkText01.AsSpan()).ToMacroString()}"); - // ImGui.Text($"Text2: {new ReadOnlySeStringSpan(textInputComponent->UnkText02.AsSpan()).ToMacroString()}"); - ImGui.Text($"AvailableLines: {new ReadOnlySeStringSpan(textInputComponent->AvailableLines.AsSpan()).ToMacroString()}"); - ImGui.Text($"HighlightedAutoTranslateOptionColorPrefix: {new ReadOnlySeStringSpan(textInputComponent->HighlightedAutoTranslateOptionColorPrefix.AsSpan()).ToMacroString()}"); - ImGui.Text($"HighlightedAutoTranslateOptionColorSuffix: {new ReadOnlySeStringSpan(textInputComponent->HighlightedAutoTranslateOptionColorSuffix.AsSpan()).ToMacroString()}"); + ImGui.Text( + $"InputBase Text1: {Marshal.PtrToStringAnsi(new(textInputComponent->AtkComponentInputBase.EvaluatedString.StringPtr))}"); + ImGui.Text( + $"InputBase Text2: {Marshal.PtrToStringAnsi(new(textInputComponent->AtkComponentInputBase.RawString.StringPtr))}"); + ImGui.Text( + $"Text1: {Marshal.PtrToStringAnsi(new(textInputComponent->UnkText01.StringPtr))}"); + ImGui.Text( + $"Text2: {Marshal.PtrToStringAnsi(new(textInputComponent->UnkText02.StringPtr))}"); + ImGui.Text( + $"AvailableLines: {Marshal.PtrToStringAnsi(new(textInputComponent->AvailableLines.StringPtr))}"); + ImGui.Text( + $"HighlightedAutoTranslateOptionColorPrefix: {Marshal.PtrToStringAnsi(new(textInputComponent->HighlightedAutoTranslateOptionColorPrefix.StringPtr))}"); + ImGui.Text( + $"HighlightedAutoTranslateOptionColorSuffix: {Marshal.PtrToStringAnsi(new(textInputComponent->HighlightedAutoTranslateOptionColorSuffix.StringPtr))}"); break; case List: case TreeList: diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Counter.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Counter.cs similarity index 77% rename from Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Counter.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Counter.cs index 2ffcad5de..ff40db37a 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Counter.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Counter.cs @@ -1,11 +1,9 @@ using FFXIVClientStructs.FFXIV.Component.GUI; -using Lumina.Text.ReadOnly; - -using static Dalamud.Interface.Internal.UiDebug.Utility.Gui; +using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui; using static Dalamud.Utility.Util; -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <summary> /// A tree for an <see cref="AtkCounterNode"/> that can be printed and browsed via ImGui. @@ -32,7 +30,7 @@ internal unsafe partial class CounterNodeTree : ResNodeTree { if (!isEditorOpen) { - PrintFieldValuePairs(("Text", new ReadOnlySeStringSpan(((AtkCounterNode*)this.Node)->NodeText.AsSpan()).ToMacroString())); + PrintFieldValuePairs(("Text", ((AtkCounterNode*)this.Node)->NodeText.ToString())); } } } diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Editor.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Editor.cs similarity index 95% rename from Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Editor.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Editor.cs index d9dd1378c..ae6f5fffa 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Editor.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Editor.cs @@ -3,23 +3,20 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Components; -using Dalamud.Interface.Internal.UiDebug.Utility; +using Dalamud.Interface.Internal.UiDebug2.Utility; using Dalamud.Interface.Utility.Raii; - using FFXIVClientStructs.FFXIV.Component.GUI; -using Lumina.Text.ReadOnly; - using static Dalamud.Bindings.ImGui.ImGuiColorEditFlags; using static Dalamud.Bindings.ImGui.ImGuiInputTextFlags; using static Dalamud.Bindings.ImGui.ImGuiTableColumnFlags; using static Dalamud.Bindings.ImGui.ImGuiTableFlags; using static Dalamud.Interface.ColorHelpers; using static Dalamud.Interface.FontAwesomeIcon; -using static Dalamud.Interface.Internal.UiDebug.Utility.Gui; +using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui; using static Dalamud.Interface.Utility.ImGuiHelpers; -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <inheritdoc cref="ResNodeTree"/> internal unsafe partial class ResNodeTree @@ -30,10 +27,10 @@ internal unsafe partial class ResNodeTree private protected void DrawNodeEditorTable() { using var tbl = ImRaii.Table($"###Editor{(nint)this.Node}", 2, SizingStretchProp | NoHostExtendX); - if (!tbl.Success) - return; - - this.DrawEditorRows(); + if (tbl.Success) + { + this.DrawEditorRows(); + } } /// <summary> @@ -62,7 +59,7 @@ internal unsafe partial class ResNodeTree ImGui.TableNextColumn(); ImGui.SetNextItemWidth(150); - if (ImGui.DragFloat2($"##{(nint)this.Node:X}position", ref pos, 1, 0, 0, "%.0f")) + if (ImGui.DragFloat2($"##{(nint)this.Node:X}position", ref pos, 1, default, default, "%.0f")) { this.Node->X = pos.X; this.Node->Y = pos.Y; @@ -76,7 +73,7 @@ internal unsafe partial class ResNodeTree ImGui.Text("Size:"u8); ImGui.TableNextColumn(); ImGui.SetNextItemWidth(150); - if (ImGui.DragFloat2($"##{(nint)this.Node:X}size", ref size, 1, 0, 0, "%.0f")) + if (ImGui.DragFloat2($"##{(nint)this.Node:X}size", ref size, 1, 0, default, "%.0f")) { this.Node->Width = (ushort)Math.Max(size.X, 0); this.Node->Height = (ushort)Math.Max(size.Y, 0); @@ -104,7 +101,7 @@ internal unsafe partial class ResNodeTree ImGui.Text("Origin:"u8); ImGui.TableNextColumn(); ImGui.SetNextItemWidth(150); - if (ImGui.DragFloat2($"##{(nint)this.Node:X}origin", ref origin, 1, 0, 0, "%.0f")) + if (ImGui.DragFloat2($"##{(nint)this.Node:X}origin", ref origin, 1, default, default, "%.0f")) { this.Node->OriginX = origin.X; this.Node->OriginY = origin.Y; @@ -123,7 +120,7 @@ internal unsafe partial class ResNodeTree angle -= 360; } - if (ImGui.DragFloat($"##{(nint)this.Node:X}rotation", ref angle, 0.05f, 0, 0, "%.2f°")) + if (ImGui.DragFloat($"##{(nint)this.Node:X}rotation", ref angle, 0.05f, default, default, "%.2f°")) { this.Node->Rotation = (float)(angle / (180 / Math.PI)); this.Node->DrawFlags |= 0xD; @@ -171,6 +168,7 @@ internal unsafe partial class ResNodeTree ImGui.Text("Add:"u8); ImGui.TableNextColumn(); ImGui.SetNextItemWidth(124); + if (ImGui.DragFloat3($"##{(nint)this.Node:X}addRGB", ref add, 1, -255, 255, "%.0f")) { this.Node->AddRed = (short)add.X; @@ -201,7 +199,7 @@ internal unsafe partial class CounterNodeTree { base.DrawEditorRows(); - var str = new ReadOnlySeStringSpan(this.CntNode->NodeText.AsSpan()).ToMacroString(); + var str = this.CntNode->NodeText.ToString(); ImGui.TableNextRow(); ImGui.TableNextColumn(); @@ -301,7 +299,7 @@ internal unsafe partial class TextNodeTree { base.DrawEditorRows(); - var text = new ReadOnlySeStringSpan(this.TxtNode->NodeText.AsSpan()).ToMacroString(); + var text = this.TxtNode->NodeText.ToString(); var fontIndex = FontList.IndexOf(this.TxtNode->FontType); int fontSize = this.TxtNode->FontSize; var alignment = this.TxtNode->AlignmentType; diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Image.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Image.cs similarity index 94% rename from Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Image.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Image.cs index 8c9207f5d..260ea4942 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Image.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Image.cs @@ -2,8 +2,8 @@ using System.Numerics; using System.Runtime.InteropServices; using Dalamud.Bindings.ImGui; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; - using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using FFXIVClientStructs.FFXIV.Component.GUI; @@ -11,11 +11,11 @@ using static Dalamud.Bindings.ImGui.ImGuiTableColumnFlags; using static Dalamud.Bindings.ImGui.ImGuiTableFlags; using static Dalamud.Bindings.ImGui.ImGuiTreeNodeFlags; using static Dalamud.Interface.ColorHelpers; -using static Dalamud.Interface.Internal.UiDebug.Utility.Gui; +using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui; using static Dalamud.Utility.Util; using static FFXIVClientStructs.FFXIV.Component.GUI.TextureType; -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <summary> /// A tree for an <see cref="AtkImageNode"/> that can be printed and browsed via ImGui. @@ -64,6 +64,7 @@ internal unsafe partial class ImageNodeTree : ResNodeTree } using var tree = ImRaii.TreeNode($"Texture##texture{(nint)this.TexData.Texture->D3D11ShaderResourceView:X}", SpanFullWidth); + if (tree.Success) { PrintFieldValuePairs( @@ -297,11 +298,17 @@ internal unsafe partial class ImageNodeTree : ResNodeTree } this.TexType = asset->AtkTexture.TextureType; - this.Texture = asset->AtkTexture.IsTextureReady() ? asset->AtkTexture.GetKernelTexture() : null; - if (this.TexType == Resource && asset->AtkTexture.Resource != null && asset->AtkTexture.Resource->TexFileResourceHandle != null) + if (this.TexType == Resource) { - this.Path = asset->AtkTexture.Resource->TexFileResourceHandle->ResourceHandle.FileName.ToString(); + var resource = asset->AtkTexture.Resource; + this.Texture = resource->KernelTextureObject; + this.Path = Marshal.PtrToStringAnsi(new(resource->TexFileResourceHandle->ResourceHandle.FileName.BufferPtr)); + } + else + { + this.Texture = this.TexType == KernelTexture ? asset->AtkTexture.KernelTexture : null; + this.Path = null; } this.HiRes = this.Path?.Contains("_hr1") ?? false; diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.NineGrid.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.NineGrid.cs similarity index 92% rename from Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.NineGrid.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.NineGrid.cs index 7d241ebdb..489135ed0 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.NineGrid.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.NineGrid.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; -using Dalamud.Interface.Internal.UiDebug.Utility; +using Dalamud.Interface.Internal.UiDebug2.Utility; +using Dalamud.Interface.Utility; using FFXIVClientStructs.FFXIV.Component.GUI; @@ -9,7 +10,7 @@ using static Dalamud.Utility.Util; using Vector2 = System.Numerics.Vector2; using Vector4 = System.Numerics.Vector4; -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <summary> /// A tree for an <see cref="AtkNineGridNode"/> that can be printed and browsed via ImGui. @@ -60,10 +61,10 @@ internal unsafe partial class NineGridNodeTree : ImageNodeTree var ngCol = RgbaVector4ToUint(col with { W = 0.75f * col.W }); - var windowDrawList = ImGui.GetWindowDrawList(); - windowDrawList.AddRect(partBegin, partEnd, RgbaVector4ToUint(col)); - windowDrawList.AddRect(ngBegin1, ngEnd1, ngCol); - windowDrawList.AddRect(ngBegin2, ngEnd2, ngCol); + ImGui.GetWindowDrawList() + .AddRect(partBegin, partEnd, RgbaVector4ToUint(col)); + ImGui.GetWindowDrawList().AddRect(ngBegin1, ngEnd1, ngCol); + ImGui.GetWindowDrawList().AddRect(ngBegin2, ngEnd2, ngCol); ImGui.SetCursorPos(cursorLocalPos + uv + new Vector2(0, -20)); ImGui.TextColored(col, $"[#{partId}]\t{part.U}, {part.V}\t{part.Width}x{part.Height}"); diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Res.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Res.cs similarity index 97% rename from Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Res.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Res.cs index 3ff64fd24..418156811 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Res.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Res.cs @@ -4,23 +4,23 @@ using System.Runtime.InteropServices; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Components; -using Dalamud.Interface.Internal.UiDebug.Utility; +using Dalamud.Interface.Internal.UiDebug2.Utility; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; - using FFXIVClientStructs.FFXIV.Component.GUI; using static Dalamud.Bindings.ImGui.ImGuiCol; using static Dalamud.Bindings.ImGui.ImGuiTreeNodeFlags; using static Dalamud.Interface.ColorHelpers; using static Dalamud.Interface.FontAwesomeIcon; -using static Dalamud.Interface.Internal.UiDebug.Browsing.Events; -using static Dalamud.Interface.Internal.UiDebug.ElementSelector; -using static Dalamud.Interface.Internal.UiDebug.UiDebug; -using static Dalamud.Interface.Internal.UiDebug.Utility.Gui; +using static Dalamud.Interface.Internal.UiDebug2.Browsing.Events; +using static Dalamud.Interface.Internal.UiDebug2.ElementSelector; +using static Dalamud.Interface.Internal.UiDebug2.UiDebug2; +using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui; using static Dalamud.Utility.Util; using static FFXIVClientStructs.FFXIV.Component.GUI.NodeFlags; -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <summary> /// A tree for an <see cref="AtkResNode"/> that can be printed and browsed via ImGui. @@ -138,6 +138,7 @@ internal unsafe partial class ResNodeTree : IDisposable PrintNodeList(nodeList, count, addonTree); var lineEnd = lineStart with { Y = ImGui.GetCursorScreenPos().Y - 7 }; + if (lineStart.Y < lineEnd.Y) { ImGui.GetWindowDrawList().AddLine(lineStart, lineEnd, RgbaVector4ToUint(color), 1); diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Text.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Text.cs similarity index 72% rename from Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Text.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Text.cs index 74e14b683..1435335db 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/NodeTree.Text.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Text.cs @@ -2,20 +2,20 @@ using System.Numerics; using System.Runtime.InteropServices; using Dalamud.Bindings.ImGui; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Interface.ImGuiSeStringRenderer; +using Dalamud.Interface.Internal.UiDebug2.Utility; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; - using FFXIVClientStructs.FFXIV.Client.System.String; using FFXIVClientStructs.FFXIV.Component.GUI; -using Lumina.Text.ReadOnly; - using static Dalamud.Interface.ColorHelpers; -using static Dalamud.Interface.Internal.UiDebug.Utility.Gui; +using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui; using static Dalamud.Utility.Util; -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <summary> /// A tree for an <see cref="AtkTextNode"/> that can be printed and browsed via ImGui. @@ -64,7 +64,7 @@ internal unsafe partial class TextNodeTree : ResNodeTree } catch { - ImGui.Text(new ReadOnlySeStringSpan(this.NodeText.AsSpan()).ToMacroString()); + ImGui.Text(Marshal.PtrToStringAnsi(new(this.NodeText.StringPtr)) ?? string.Empty); } PrintFieldValuePairs( @@ -82,24 +82,36 @@ internal unsafe partial class TextNodeTree : ResNodeTree private void PrintPayloads() { using var tree = ImRaii.TreeNode($"Text Payloads##{(nint)this.Node:X}"); + if (tree.Success) { - var idx = 0; - foreach (var payload in new ReadOnlySeString(this.NodeText.AsSpan())) + var utf8String = this.NodeText; + var seStringBytes = new byte[utf8String.BufUsed]; + for (var i = 0L; i < utf8String.BufUsed; i++) { - ImGui.Text($"[{idx}]"); + seStringBytes[i] = utf8String.StringPtr.Value[i]; + } + + var seString = SeString.Parse(seStringBytes); + for (var i = 0; i < seString.Payloads.Count; i++) + { + var payload = seString.Payloads[i]; + ImGui.Text($"[{i}]"); ImGui.SameLine(); switch (payload.Type) { - case ReadOnlySePayloadType.Text: - PrintFieldValuePair("Raw Text", payload.ToString()); + case PayloadType.RawText when payload is TextPayload tp: + { + Gui.PrintFieldValuePair("Raw Text", tp.Text ?? string.Empty); break; + } + default: + { ImGui.Text(payload.ToString()); break; + } } - - idx++; } } } diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/TimelineTree.KeyGroupColumn.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/TimelineTree.KeyGroupColumn.cs similarity index 98% rename from Dalamud/Interface/Internal/UiDebug/Browsing/TimelineTree.KeyGroupColumn.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/TimelineTree.KeyGroupColumn.cs index 910762d97..71323088b 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/TimelineTree.KeyGroupColumn.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/TimelineTree.KeyGroupColumn.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Dalamud.Bindings.ImGui; -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <inheritdoc cref="TimelineTree"/> public readonly partial struct TimelineTree diff --git a/Dalamud/Interface/Internal/UiDebug/Browsing/TimelineTree.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/TimelineTree.cs similarity index 99% rename from Dalamud/Interface/Internal/UiDebug/Browsing/TimelineTree.cs rename to Dalamud/Interface/Internal/UiDebug2/Browsing/TimelineTree.cs index a4d7151c0..21f4fb54a 100644 --- a/Dalamud/Interface/Internal/UiDebug/Browsing/TimelineTree.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/TimelineTree.cs @@ -5,7 +5,6 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; - using FFXIVClientStructs.FFXIV.Client.Graphics; using FFXIVClientStructs.FFXIV.Component.GUI; @@ -13,12 +12,12 @@ using static Dalamud.Bindings.ImGui.ImGuiTableColumnFlags; using static Dalamud.Bindings.ImGui.ImGuiTableFlags; using static Dalamud.Bindings.ImGui.ImGuiTreeNodeFlags; using static Dalamud.Interface.ColorHelpers; -using static Dalamud.Interface.Internal.UiDebug.Utility.Gui; +using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui; using static Dalamud.Utility.Util; using static FFXIVClientStructs.FFXIV.Component.GUI.NodeType; // ReSharper disable SuggestBaseTypeForParameter -namespace Dalamud.Interface.Internal.UiDebug.Browsing; +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// <summary> /// A struct allowing a node's animation timeline to be printed and browsed. @@ -58,6 +57,7 @@ public readonly unsafe partial struct TimelineTree if (animationCount > 0) { using var tree = ImRaii.TreeNode($"Timeline##{(nint)this.node:X}timeline", SpanFullWidth); + if (tree.Success) { PrintFieldValuePair("Timeline", $"{(nint)this.NodeTimeline:X}"); @@ -89,6 +89,7 @@ public readonly unsafe partial struct TimelineTree if (labelSetCount > 0 && this.Resource->LabelSets is not null) { using var tree = ImRaii.TreeNode($"Timeline Label Sets##{(nint)this.node:X}LabelSets", SpanFullWidth); + if (tree.Success) { this.DrawLabelSets(); @@ -323,6 +324,7 @@ public readonly unsafe partial struct TimelineTree using (ImRaii.PushColor(ImGuiCol.Text, new Vector4(1, 0.65F, 0.4F, 1), isActive)) { using var tree = ImRaii.TreeNode($"[#{a}] [Frames {animation.StartFrameIdx}-{animation.EndFrameIdx}] {(isActive ? " (Active)" : string.Empty)}###{(nint)this.node}animTree{a}"); + if (tree.Success) { PrintFieldValuePair("Animation", $"{address:X}"); @@ -332,6 +334,7 @@ public readonly unsafe partial struct TimelineTree if (columns.Count > 0) { using var tbl = ImRaii.Table($"##{(nint)this.node}animTable{a}", columns.Count, Borders | SizingFixedFit | RowBg | NoHostExtendX); + if (tbl.Success) { foreach (var c in columns) diff --git a/Dalamud/Interface/Internal/UiDebug/ElementSelector.cs b/Dalamud/Interface/Internal/UiDebug2/ElementSelector.cs similarity index 83% rename from Dalamud/Interface/Internal/UiDebug/ElementSelector.cs rename to Dalamud/Interface/Internal/UiDebug2/ElementSelector.cs index 808ff25d7..2ea8fd5d2 100644 --- a/Dalamud/Interface/Internal/UiDebug/ElementSelector.cs +++ b/Dalamud/Interface/Internal/UiDebug2/ElementSelector.cs @@ -5,10 +5,9 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Components; -using Dalamud.Interface.Internal.UiDebug.Browsing; -using Dalamud.Interface.Internal.UiDebug.Utility; +using Dalamud.Interface.Internal.UiDebug2.Browsing; +using Dalamud.Interface.Internal.UiDebug2.Utility; using Dalamud.Interface.Utility.Raii; - using FFXIVClientStructs.FFXIV.Component.GUI; using static System.Globalization.NumberFormatInfo; @@ -16,7 +15,7 @@ using static System.Globalization.NumberFormatInfo; using static Dalamud.Bindings.ImGui.ImGuiCol; using static Dalamud.Bindings.ImGui.ImGuiWindowFlags; using static Dalamud.Interface.FontAwesomeIcon; -using static Dalamud.Interface.Internal.UiDebug.UiDebug; +using static Dalamud.Interface.Internal.UiDebug2.UiDebug2; using static Dalamud.Interface.UiBuilder; using static Dalamud.Interface.Utility.ImGuiHelpers; using static FFXIVClientStructs.FFXIV.Component.GUI.NodeFlags; @@ -25,7 +24,7 @@ using static FFXIVClientStructs.FFXIV.Component.GUI.NodeFlags; #pragma warning disable CS0659 -namespace Dalamud.Interface.Internal.UiDebug; +namespace Dalamud.Interface.Internal.UiDebug2; /// <summary> /// A tool that enables the user to select UI elements within the inspector by mousing over them onscreen. @@ -34,7 +33,7 @@ internal unsafe class ElementSelector : IDisposable { private const int UnitListCount = 18; - private readonly UiDebug uiDebug; + private readonly UiDebug2 uiDebug2; private string addressSearchInput = string.Empty; @@ -43,10 +42,10 @@ internal unsafe class ElementSelector : IDisposable /// <summary> /// Initializes a new instance of the <see cref="ElementSelector"/> class. /// </summary> - /// <param name="uiDebug">The instance of <see cref="UiDebug"/> this Element Selector belongs to.</param> - internal ElementSelector(UiDebug uiDebug) + /// <param name="uiDebug2">The instance of <see cref="UiDebug2"/> this Element Selector belongs to.</param> + internal ElementSelector(UiDebug2 uiDebug2) { - this.uiDebug = uiDebug; + this.uiDebug2 = uiDebug2; } /// <summary> @@ -160,61 +159,65 @@ internal unsafe class ElementSelector : IDisposable if (ch.Success) { using var gr = ImRaii.Group(); - Gui.PrintFieldValuePair("Mouse Position", $"{mousePos.X}, {mousePos.Y}"); - ImGui.Spacing(); - ImGui.Text("RESULTS:\n"u8); - - var i = 0; - foreach (var a in addonResults) + if (gr.Success) { - var name = a.Addon->NameString; - ImGui.Text($"[Addon] {name}"); + Gui.PrintFieldValuePair("Mouse Position", $"{mousePos.X}, {mousePos.Y}"); + ImGui.Spacing(); + ImGui.Text("RESULTS:\n"u8); - using var indent = ImRaii.PushIndent(15.0f); - foreach (var n in a.Nodes) + var i = 0; + foreach (var a in addonResults) { - var nSelected = i++ == this.index; - - PrintNodeHeaderOnly(n.Node, nSelected, a.Addon); - - if (nSelected && ImGui.IsMouseClicked(ImGuiMouseButton.Left)) + var name = a.Addon->NameString; + ImGui.Text($"[Addon] {name}"); + ImGui.Indent(15); + foreach (var n in a.Nodes) { - this.Active = false; + var nSelected = i++ == this.index; - this.uiDebug.SelectedAddonName = a.Addon->NameString; + PrintNodeHeaderOnly(n.Node, nSelected, a.Addon); - var ptrList = new List<nint> { (nint)n.Node }; - - var nextNode = n.Node->ParentNode; - while (nextNode != null) + if (nSelected && ImGui.IsMouseClicked(ImGuiMouseButton.Left)) { - ptrList.Add((nint)nextNode); - nextNode = nextNode->ParentNode; + this.Active = false; + + this.uiDebug2.SelectedAddonName = a.Addon->NameString; + + var ptrList = new List<nint> { (nint)n.Node }; + + var nextNode = n.Node->ParentNode; + while (nextNode != null) + { + ptrList.Add((nint)nextNode); + nextNode = nextNode->ParentNode; + } + + SearchResults = [.. ptrList]; + Countdown = 100; + Scrolled = false; } - SearchResults = [.. ptrList]; - Countdown = 100; - Scrolled = false; + if (nSelected) + { + n.NodeBounds.DrawFilled(new(1, 1, 0.2f, 1)); + } } - if (nSelected) + ImGui.Indent(-15); + } + + if (i != 0) + { + this.index -= (int)ImGui.GetIO().MouseWheel; + while (this.index < 0) { - n.NodeBounds.DrawFilled(new(1, 1, 0.2f, 1)); + this.index += i; } - } - } - if (i != 0) - { - this.index -= (int)ImGui.GetIO().MouseWheel; - while (this.index < 0) - { - this.index += i; - } - - while (this.index >= i) - { - this.index -= i; + while (this.index >= i) + { + this.index -= i; + } } } } @@ -420,7 +423,7 @@ internal unsafe class ElementSelector : IDisposable var addon = unitManager->Entries[j].Value; if ((nint)addon == address || FindByAddress(addon, address)) { - this.uiDebug.SelectedAddonName = addon->NameString; + this.uiDebug2.SelectedAddonName = addon->NameString; return; } } diff --git a/Dalamud/Interface/Internal/UiDebug/Popout.Addon.cs b/Dalamud/Interface/Internal/UiDebug2/Popout.Addon.cs similarity index 88% rename from Dalamud/Interface/Internal/UiDebug/Popout.Addon.cs rename to Dalamud/Interface/Internal/UiDebug2/Popout.Addon.cs index cc80a27c4..4684caa60 100644 --- a/Dalamud/Interface/Internal/UiDebug/Popout.Addon.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Popout.Addon.cs @@ -1,11 +1,11 @@ using System.Numerics; using Dalamud.Bindings.ImGui; -using Dalamud.Interface.Internal.UiDebug.Browsing; +using Dalamud.Interface.Internal.UiDebug2.Browsing; using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; -namespace Dalamud.Interface.Internal.UiDebug; +namespace Dalamud.Interface.Internal.UiDebug2; /// <summary> /// A popout window for an <see cref="AddonTree"/>. @@ -39,7 +39,7 @@ internal class AddonPopoutWindow : Window, IDisposable /// <inheritdoc/> public override void Draw() { - using var ch = ImRaii.Child($"{this.WindowName}child", Vector2.Zero, true); + using var ch = ImRaii.Child($"{this.WindowName}child", new(-1, -1), true); if (ch.Success) { this.addonTree.Draw(); diff --git a/Dalamud/Interface/Internal/UiDebug/Popout.Node.cs b/Dalamud/Interface/Internal/UiDebug2/Popout.Node.cs similarity index 88% rename from Dalamud/Interface/Internal/UiDebug/Popout.Node.cs rename to Dalamud/Interface/Internal/UiDebug2/Popout.Node.cs index 7d955f7f5..da4b95256 100644 --- a/Dalamud/Interface/Internal/UiDebug/Popout.Node.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Popout.Node.cs @@ -1,15 +1,14 @@ using System.Numerics; using Dalamud.Bindings.ImGui; -using Dalamud.Interface.Internal.UiDebug.Browsing; +using Dalamud.Interface.Internal.UiDebug2.Browsing; using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; - using FFXIVClientStructs.FFXIV.Component.GUI; -using static Dalamud.Interface.Internal.UiDebug.UiDebug; +using static Dalamud.Interface.Internal.UiDebug2.UiDebug2; -namespace Dalamud.Interface.Internal.UiDebug; +namespace Dalamud.Interface.Internal.UiDebug2; /// <summary> /// A popout window for a <see cref="ResNodeTree"/>. @@ -39,7 +38,7 @@ internal unsafe class NodePopoutWindow : Window, IDisposable this.PositionCondition = ImGuiCond.Once; this.SizeCondition = ImGuiCond.Once; this.Size = new(700, 200); - this.SizeConstraints = new() { MinimumSize = new Vector2(100, 100) }; + this.SizeConstraints = new() { MinimumSize = new(100, 100) }; } private AddonTree AddonTree => this.resNodeTree.AddonTree; @@ -51,7 +50,7 @@ internal unsafe class NodePopoutWindow : Window, IDisposable { if (this.Node != null && this.AddonTree.ContainsNode(this.Node)) { - using var ch = ImRaii.Child($"{(nint)this.Node:X}popoutChild", Vector2.Zero, true); + using var ch = ImRaii.Child($"{(nint)this.Node:X}popoutChild", new(-1, -1), true); if (ch.Success) { ResNodeTree.GetOrCreate(this.Node, this.AddonTree).Print(null, this.firstDraw); diff --git a/Dalamud/Interface/Internal/UiDebug/UiDebug.Sidebar.cs b/Dalamud/Interface/Internal/UiDebug2/UiDebug2.Sidebar.cs similarity index 98% rename from Dalamud/Interface/Internal/UiDebug/UiDebug.Sidebar.cs rename to Dalamud/Interface/Internal/UiDebug2/UiDebug2.Sidebar.cs index 0a24d4572..210da896f 100644 --- a/Dalamud/Interface/Internal/UiDebug/UiDebug.Sidebar.cs +++ b/Dalamud/Interface/Internal/UiDebug2/UiDebug2.Sidebar.cs @@ -4,7 +4,6 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Components; using Dalamud.Interface.Utility.Raii; - using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Component.GUI; @@ -12,10 +11,10 @@ using static System.StringComparison; using static Dalamud.Interface.FontAwesomeIcon; -namespace Dalamud.Interface.Internal.UiDebug; +namespace Dalamud.Interface.Internal.UiDebug2; -/// <inheritdoc cref="UiDebug"/> -internal unsafe partial class UiDebug +/// <inheritdoc cref="UiDebug2"/> +internal unsafe partial class UiDebug2 { /// <summary> /// All unit lists to check for addons. diff --git a/Dalamud/Interface/Internal/UiDebug/UiDebug.cs b/Dalamud/Interface/Internal/UiDebug2/UiDebug2.cs similarity index 90% rename from Dalamud/Interface/Internal/UiDebug/UiDebug.cs rename to Dalamud/Interface/Internal/UiDebug2/UiDebug2.cs index bd7426466..ce4c6dfeb 100644 --- a/Dalamud/Interface/Internal/UiDebug/UiDebug.cs +++ b/Dalamud/Interface/Internal/UiDebug2/UiDebug2.cs @@ -2,17 +2,16 @@ using System.Collections.Generic; using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui; -using Dalamud.Interface.Internal.UiDebug.Browsing; +using Dalamud.Interface.Internal.UiDebug2.Browsing; using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; using Dalamud.Logging.Internal; using Dalamud.Plugin.Services; - using FFXIVClientStructs.FFXIV.Component.GUI; using static Dalamud.Bindings.ImGui.ImGuiWindowFlags; -namespace Dalamud.Interface.Internal.UiDebug; +namespace Dalamud.Interface.Internal.UiDebug2; // Original version by aers https://github.com/aers/FFXIVUIDebug // Also incorporates features from Caraxi's fork https://github.com/Caraxi/SimpleTweaksPlugin/blob/main/Debugging/UIDebug.cs @@ -20,21 +19,21 @@ namespace Dalamud.Interface.Internal.UiDebug; /// <summary> /// A tool for browsing the contents and structure of UI elements. /// </summary> -internal partial class UiDebug : IDisposable +internal partial class UiDebug2 : IDisposable { - /// <inheritdoc cref="ModuleLog"/> - internal static readonly ModuleLog Log = ModuleLog.Create<UiDebug>(); - private readonly ElementSelector elementSelector; /// <summary> - /// Initializes a new instance of the <see cref="UiDebug"/> class. + /// Initializes a new instance of the <see cref="UiDebug2"/> class. /// </summary> - internal UiDebug() + internal UiDebug2() { this.elementSelector = new(this); } + /// <inheritdoc cref="ModuleLog"/> + internal static ModuleLog Log { get; set; } = new("UiDebug2"); + /// <inheritdoc cref="IGameGui"/> internal static IGameGui GameGui { get; set; } = Service<GameGui>.Get(); diff --git a/Dalamud/Interface/Internal/UiDebug/Utility/Gui.cs b/Dalamud/Interface/Internal/UiDebug2/Utility/Gui.cs similarity index 91% rename from Dalamud/Interface/Internal/UiDebug/Utility/Gui.cs rename to Dalamud/Interface/Internal/UiDebug2/Utility/Gui.cs index 2c02fd793..da5f30e68 100644 --- a/Dalamud/Interface/Internal/UiDebug/Utility/Gui.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Utility/Gui.cs @@ -3,16 +3,15 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; - using FFXIVClientStructs.FFXIV.Client.Graphics; using static Dalamud.Bindings.ImGui.ImGuiCol; using static Dalamud.Interface.ColorHelpers; -namespace Dalamud.Interface.Internal.UiDebug.Utility; +namespace Dalamud.Interface.Internal.UiDebug2.Utility; /// <summary> -/// Miscellaneous ImGui tools used by <see cref="UiDebug"/>. +/// Miscellaneous ImGui tools used by <see cref="UiDebug2"/>. /// </summary> internal static class Gui { @@ -105,8 +104,12 @@ internal static class Gui var index = (int)Math.Floor(prog * tooltips.Length); - using var tooltip = ImRaii.Tooltip(); - ImGui.Text(tooltips[index]); + using var tt = ImRaii.Tooltip(); + + if (tt.Success) + { + ImGui.Text(tooltips[index]); + } return true; } @@ -120,14 +123,13 @@ internal static class Gui { if ((mask & 0b10) > 0) { - ImGuiHelpers.ScaledDummy(padding); + ImGui.Dummy(new(padding * ImGui.GetIO().FontGlobalScale)); } ImGui.Separator(); - if ((mask & 0b01) > 0) { - ImGuiHelpers.ScaledDummy(padding); + ImGui.Dummy(new(padding * ImGui.GetIO().FontGlobalScale)); } } } diff --git a/Dalamud/Interface/Internal/UiDebug/Utility/NodeBounds.cs b/Dalamud/Interface/Internal/UiDebug2/Utility/NodeBounds.cs similarity index 81% rename from Dalamud/Interface/Internal/UiDebug/Utility/NodeBounds.cs rename to Dalamud/Interface/Internal/UiDebug2/Utility/NodeBounds.cs index 414d49cf5..832c7f357 100644 --- a/Dalamud/Interface/Internal/UiDebug/Utility/NodeBounds.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Utility/NodeBounds.cs @@ -4,14 +4,13 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Utility; - using FFXIVClientStructs.FFXIV.Component.GUI; using static System.MathF; using static Dalamud.Interface.ColorHelpers; -namespace Dalamud.Interface.Internal.UiDebug.Utility; +namespace Dalamud.Interface.Internal.UiDebug2.Utility; /// <summary> /// A struct representing the perimeter of an <see cref="AtkResNode"/>, accounting for all transformations. @@ -62,11 +61,10 @@ public unsafe struct NodeBounds return; } - var backgroundDrawList = ImGui.GetBackgroundDrawList(); if (this.Points.Count == 1) { - backgroundDrawList.AddCircle(this.Points[0], 10, RgbaVector4ToUint(col with { W = col.W / 2 }), 12, thickness); - backgroundDrawList.AddCircle(this.Points[0], thickness, RgbaVector4ToUint(col), 12, thickness + 1); + ImGui.GetBackgroundDrawList().AddCircle(this.Points[0], 10, RgbaVector4ToUint(col with { W = col.W / 2 }), 12, thickness); + ImGui.GetBackgroundDrawList().AddCircle(this.Points[0], thickness, RgbaVector4ToUint(col), 12, thickness + 1); } else { @@ -76,7 +74,8 @@ public unsafe struct NodeBounds path.Add(p); } - backgroundDrawList.AddPolyline(ref path[0], path.Length, RgbaVector4ToUint(col), ImDrawFlags.Closed, thickness); + ImGui.GetBackgroundDrawList() + .AddPolyline(ref path[0], path.Length, RgbaVector4ToUint(col), ImDrawFlags.Closed, thickness); path.Dispose(); } @@ -94,11 +93,11 @@ public unsafe struct NodeBounds return; } - var backgroundDrawList = ImGui.GetBackgroundDrawList(); if (this.Points.Count == 1) { - backgroundDrawList.AddCircleFilled(this.Points[0], 10, RgbaVector4ToUint(col with { W = col.W / 2 }), 12); - backgroundDrawList.AddCircle(this.Points[0], 10, RgbaVector4ToUint(col), 12, thickness); + ImGui.GetBackgroundDrawList() + .AddCircleFilled(this.Points[0], 10, RgbaVector4ToUint(col with { W = col.W / 2 }), 12); + ImGui.GetBackgroundDrawList().AddCircle(this.Points[0], 10, RgbaVector4ToUint(col), 12, thickness); } else { @@ -108,8 +107,10 @@ public unsafe struct NodeBounds path.Add(p); } - backgroundDrawList.AddConvexPolyFilled(ref path[0], path.Length, RgbaVector4ToUint(col with { W = col.W / 2 })); - backgroundDrawList.AddPolyline(ref path[0], path.Length, RgbaVector4ToUint(col), ImDrawFlags.Closed, thickness); + ImGui.GetBackgroundDrawList() + .AddConvexPolyFilled(ref path[0], path.Length, RgbaVector4ToUint(col with { W = col.W / 2 })); + ImGui.GetBackgroundDrawList() + .AddPolyline(ref path[0], path.Length, RgbaVector4ToUint(col), ImDrawFlags.Closed, thickness); path.Dispose(); } diff --git a/Dalamud/Interface/Internal/Windows/BranchSwitcherWindow.cs b/Dalamud/Interface/Internal/Windows/BranchSwitcherWindow.cs index 51a9c48a6..51ff3bdcd 100644 --- a/Dalamud/Interface/Internal/Windows/BranchSwitcherWindow.cs +++ b/Dalamud/Interface/Internal/Windows/BranchSwitcherWindow.cs @@ -6,6 +6,7 @@ using System.Net.Http.Json; using System.Threading.Tasks; using Dalamud.Bindings.ImGui; +using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; @@ -14,8 +15,6 @@ using Dalamud.Utility; using Newtonsoft.Json; -using Serilog; - namespace Dalamud.Interface.Internal.Windows; /// <summary> @@ -47,8 +46,8 @@ public class BranchSwitcherWindow : Window this.branches = await client.GetFromJsonAsync<Dictionary<string, VersionEntry>>(BranchInfoUrl); Debug.Assert(this.branches != null, "this.branches != null"); - var trackName = Versioning.GetActiveTrack(); - this.selectedBranchIndex = this.branches.IndexOf(x => x.Value.Track == trackName); + var trackName = Util.GetActiveTrack(); + this.selectedBranchIndex = this.branches.IndexOf(x => x.Value.Track != trackName); if (this.selectedBranchIndex == -1) { this.selectedBranchIndex = 0; @@ -87,9 +86,12 @@ public class BranchSwitcherWindow : Window if (ImGui.Button("Pick & Restart"u8)) { - var newTrackName = pickedBranch.Key; - var newTrackKey = pickedBranch.Value.Key; - Log.Verbose("Switching to branch {Branch} with key {Key}", newTrackName, newTrackKey); + var config = Service<DalamudConfiguration>.Get(); + config.DalamudBetaKind = pickedBranch.Key; + config.DalamudBetaKey = pickedBranch.Value.Key; + + // If we exit immediately, we need to write out the new config now + config.ForceSave(); var appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); var xlPath = Path.Combine(appData, "XIVLauncher", "current", "XIVLauncher.exe"); @@ -102,8 +104,8 @@ public class BranchSwitcherWindow : Window UseShellExecute = false, }; - ps.ArgumentList.Add($"--dalamud-beta-kind={newTrackName}"); - ps.ArgumentList.Add($"--dalamud-beta-key={(newTrackKey.IsNullOrEmpty() ? "invalid" : newTrackKey)}"); + ps.ArgumentList.Add($"--dalamud-beta-kind={config.DalamudBetaKind}"); + ps.ArgumentList.Add($"--dalamud-beta-key={config.DalamudBetaKey}"); Process.Start(ps); Environment.Exit(0); diff --git a/Dalamud/Interface/Internal/Windows/ChangelogWindow.cs b/Dalamud/Interface/Internal/Windows/ChangelogWindow.cs index c67eebfec..b0a910ead 100644 --- a/Dalamud/Interface/Internal/Windows/ChangelogWindow.cs +++ b/Dalamud/Interface/Internal/Windows/ChangelogWindow.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Numerics; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Game; @@ -23,7 +22,6 @@ using Dalamud.Plugin.Internal.AutoUpdate; using Dalamud.Plugin.Services; using Dalamud.Storage.Assets; using Dalamud.Utility; - using FFXIVClientStructs.FFXIV.Client.UI; namespace Dalamud.Interface.Internal.Windows; @@ -87,7 +85,7 @@ internal sealed class ChangelogWindow : Window, IDisposable private AutoUpdateBehavior? chosenAutoUpdateBehavior; - private Dictionary<string, int> currentFtueLevels = []; + private Dictionary<string, int> currentFtueLevels = new(); private DateTime? isEligibleSince; private bool openedThroughEligibility; @@ -149,7 +147,7 @@ internal sealed class ChangelogWindow : Window, IDisposable var pmWantsChangelog = pm?.InstalledPlugins.Any() ?? true; return (string.IsNullOrEmpty(configuration.LastChangelogMajorMinor) || (!WarrantsChangelogForMajorMinor.StartsWith(configuration.LastChangelogMajorMinor) && - Versioning.GetAssemblyVersion().StartsWith(WarrantsChangelogForMajorMinor))) && pmWantsChangelog; + Util.AssemblyVersion.StartsWith(WarrantsChangelogForMajorMinor))) && pmWantsChangelog; } /// <inheritdoc/> @@ -359,7 +357,7 @@ internal sealed class ChangelogWindow : Window, IDisposable { case State.WindowFadeIn: case State.ExplainerIntro: - ImGui.TextWrapped($"Welcome to Dalamud v{Versioning.GetScmVersion()}!"); + ImGui.TextWrapped($"Welcome to Dalamud v{Util.GetScmVersion()}!"); ImGuiHelpers.ScaledDummy(5); ImGui.TextWrapped(ChangeLog); ImGuiHelpers.ScaledDummy(5); diff --git a/Dalamud/Interface/Internal/Windows/ColorDemoWindow.cs b/Dalamud/Interface/Internal/Windows/ColorDemoWindow.cs index 564e8ca5e..1bff1d5c1 100644 --- a/Dalamud/Interface/Internal/Windows/ColorDemoWindow.cs +++ b/Dalamud/Interface/Internal/Windows/ColorDemoWindow.cs @@ -5,6 +5,7 @@ using System.Reflection; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; namespace Dalamud.Interface.Internal.Windows; diff --git a/Dalamud/Interface/Internal/Windows/ComponentDemoWindow.cs b/Dalamud/Interface/Internal/Windows/ComponentDemoWindow.cs index 61435f723..9096d78de 100644 --- a/Dalamud/Interface/Internal/Windows/ComponentDemoWindow.cs +++ b/Dalamud/Interface/Internal/Windows/ComponentDemoWindow.cs @@ -19,14 +19,14 @@ internal sealed class ComponentDemoWindow : Window private static readonly TimeSpan DefaultEasingTime = new(0, 0, 0, 1700); private readonly List<(string Name, Action Demo)> componentDemos; - private readonly IReadOnlyList<Easing> easings = - [ + private readonly IReadOnlyList<Easing> easings = new Easing[] + { new InSine(DefaultEasingTime), new OutSine(DefaultEasingTime), new InOutSine(DefaultEasingTime), new InCubic(DefaultEasingTime), new OutCubic(DefaultEasingTime), new InOutCubic(DefaultEasingTime), new InQuint(DefaultEasingTime), new OutQuint(DefaultEasingTime), new InOutQuint(DefaultEasingTime), new InCirc(DefaultEasingTime), new OutCirc(DefaultEasingTime), new InOutCirc(DefaultEasingTime), new InElastic(DefaultEasingTime), new OutElastic(DefaultEasingTime), new InOutElastic(DefaultEasingTime), - ]; + }; private int animationTimeMs = (int)DefaultEasingTime.TotalMilliseconds; private Vector4 defaultColor = ImGuiColors.DalamudOrange; @@ -42,14 +42,14 @@ internal sealed class ComponentDemoWindow : Window this.RespectCloseHotkey = false; - this.componentDemos = - [ + this.componentDemos = new() + { ("Test", ImGuiComponents.Test), ("HelpMarker", HelpMarkerDemo), ("IconButton", IconButtonDemo), ("TextWithLabel", TextWithLabelDemo), ("ColorPickerWithPalette", this.ColorPickerWithPaletteDemo), - ]; + }; } /// <inheritdoc/> diff --git a/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs b/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs index 36b0883bb..74df500db 100644 --- a/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs +++ b/Dalamud/Interface/Internal/Windows/ConsoleWindow.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Numerics; +using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; @@ -21,7 +22,6 @@ using Dalamud.Interface.Windowing; using Dalamud.Plugin.Internal; using Dalamud.Plugin.Services; using Dalamud.Utility; - using Serilog; using Serilog.Events; @@ -40,7 +40,7 @@ internal class ConsoleWindow : Window, IDisposable private readonly RollingList<LogEntry> logText; private readonly RollingList<LogEntry> filteredLogEntries; - private readonly List<PluginFilterEntry> pluginFilters = []; + private readonly List<PluginFilterEntry> pluginFilters = new(); private readonly DalamudConfiguration configuration; diff --git a/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs b/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs index 64de5e87d..ae86958dd 100644 --- a/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs +++ b/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs @@ -8,7 +8,6 @@ using Dalamud.Interface.Internal.Windows.Data.Widgets; using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using Dalamud.Utility; - using Serilog; namespace Dalamud.Interface.Internal.Windows.Data; @@ -19,8 +18,9 @@ namespace Dalamud.Interface.Internal.Windows.Data; internal class DataWindow : Window, IDisposable { private readonly IDataWindowWidget[] modules = - [ + { new AddonInspectorWidget(), + new AddonInspectorWidget2(), new AddonLifecycleWidget(), new AddonWidget(), new AddressesWidget(), @@ -44,7 +44,6 @@ internal class DataWindow : Window, IDisposable new ImGuiWidget(), new InventoryWidget(), new KeyStateWidget(), - new LogMessageMonitorWidget(), new MarketBoardWidget(), new NetworkMonitorWidget(), new NounProcessorWidget(), @@ -63,13 +62,13 @@ internal class DataWindow : Window, IDisposable new UiColorWidget(), new UldWidget(), new VfsWidget(), - ]; + }; private readonly IOrderedEnumerable<IDataWindowWidget> orderedModules; private bool isExcept; private bool selectionCollapsed; - + private IDataWindowWidget currentWidget; private bool isLoaded; /// <summary> @@ -83,12 +82,9 @@ internal class DataWindow : Window, IDisposable this.RespectCloseHotkey = false; this.orderedModules = this.modules.OrderBy(module => module.DisplayName); - this.CurrentWidget = this.orderedModules.First(); + this.currentWidget = this.orderedModules.First(); } - /// <summary>Gets or sets the current widget.</summary> - public IDataWindowWidget CurrentWidget { get; set; } - /// <inheritdoc/> public void Dispose() => this.modules.OfType<IDisposable>().AggregateToDisposable().Dispose(); @@ -103,20 +99,6 @@ internal class DataWindow : Window, IDisposable { } - /// <summary>Gets the data window widget of the specified type.</summary> - /// <typeparam name="T">Type of the data window widget to find.</typeparam> - /// <returns>Found widget.</returns> - public T GetWidget<T>() where T : IDataWindowWidget - { - foreach (var m in this.modules) - { - if (m is T w) - return w; - } - - throw new ArgumentException($"No widget of type {typeof(T).FullName} found."); - } - /// <summary> /// Set the DataKind dropdown menu. /// </summary> @@ -128,7 +110,7 @@ internal class DataWindow : Window, IDisposable if (this.modules.FirstOrDefault(module => module.IsWidgetCommand(dataKind)) is { } targetModule) { - this.CurrentWidget = targetModule; + this.currentWidget = targetModule; } else { @@ -171,9 +153,9 @@ internal class DataWindow : Window, IDisposable { foreach (var widget in this.orderedModules) { - if (ImGui.Selectable(widget.DisplayName, this.CurrentWidget == widget)) + if (ImGui.Selectable(widget.DisplayName, this.currentWidget == widget)) { - this.CurrentWidget = widget; + this.currentWidget = widget; } } @@ -224,9 +206,9 @@ internal class DataWindow : Window, IDisposable try { - if (this.CurrentWidget is { Ready: true }) + if (this.currentWidget is { Ready: true }) { - this.CurrentWidget.Draw(); + this.currentWidget.Draw(); } else { diff --git a/Dalamud/Interface/Internal/Windows/Data/DataWindowWidgetExtensions.cs b/Dalamud/Interface/Internal/Windows/Data/DataWindowWidgetExtensions.cs index d286c4428..a81d3edf3 100644 --- a/Dalamud/Interface/Internal/Windows/Data/DataWindowWidgetExtensions.cs +++ b/Dalamud/Interface/Internal/Windows/Data/DataWindowWidgetExtensions.cs @@ -3,6 +3,7 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification.Internal; +using Dalamud.Interface.Utility; namespace Dalamud.Interface.Internal.Windows.Data; diff --git a/Dalamud/Interface/Internal/Windows/Data/GameInventoryTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/GameInventoryTestWidget.cs index 2031c66c2..55f619e9f 100644 --- a/Dalamud/Interface/Internal/Windows/Data/GameInventoryTestWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/GameInventoryTestWidget.cs @@ -5,9 +5,9 @@ using Dalamud.Configuration.Internal; using Dalamud.Game.Inventory; using Dalamud.Game.Inventory.InventoryEventArgTypes; using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Logging.Internal; - using Serilog.Events; namespace Dalamud.Interface.Internal.Windows.Data; @@ -17,14 +17,14 @@ namespace Dalamud.Interface.Internal.Windows.Data; /// </summary> internal class GameInventoryTestWidget : IDataWindowWidget { - private static readonly ModuleLog Log = ModuleLog.Create<GameInventoryTestWidget>(); + private static readonly ModuleLog Log = new(nameof(GameInventoryTestWidget)); private GameInventoryPluginScoped? scoped; private bool standardEnabled; private bool rawEnabled; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["gameinventorytest"]; + public string[]? CommandShortcuts { get; init; } = { "gameinventorytest" }; /// <inheritdoc/> public string DisplayName { get; init; } = "GameInventory Test"; diff --git a/Dalamud/Interface/Internal/Windows/Data/WidgetUtil.cs b/Dalamud/Interface/Internal/Windows/Data/WidgetUtil.cs index 58cc672e8..bb8bd8ea1 100644 --- a/Dalamud/Interface/Internal/Windows/Data/WidgetUtil.cs +++ b/Dalamud/Interface/Internal/Windows/Data/WidgetUtil.cs @@ -1,4 +1,5 @@ using Dalamud.Bindings.ImGui; +using Dalamud.Interface.Utility; namespace Dalamud.Interface.Internal.Windows.Data; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonInspectorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonInspectorWidget.cs index 95a5616b1..e11404dec 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonInspectorWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonInspectorWidget.cs @@ -5,10 +5,10 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// </summary> internal class AddonInspectorWidget : IDataWindowWidget { - private UiDebug.UiDebug? addonInspector; - + private UiDebug? addonInspector; + /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["ai", "addoninspector"]; + public string[]? CommandShortcuts { get; init; } = { "ai", "addoninspector" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Addon Inspector"; @@ -19,7 +19,7 @@ internal class AddonInspectorWidget : IDataWindowWidget /// <inheritdoc/> public void Load() { - this.addonInspector = new UiDebug.UiDebug(); + this.addonInspector = new UiDebug(); if (this.addonInspector is not null) { diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonInspectorWidget2.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonInspectorWidget2.cs new file mode 100644 index 000000000..6cd6ecb0b --- /dev/null +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonInspectorWidget2.cs @@ -0,0 +1,35 @@ +namespace Dalamud.Interface.Internal.Windows.Data.Widgets; + +/// <summary> +/// Widget for displaying addon inspector. +/// </summary> +internal class AddonInspectorWidget2 : IDataWindowWidget +{ + private UiDebug2.UiDebug2? addonInspector2; + + /// <inheritdoc/> + public string[]? CommandShortcuts { get; init; } = ["ai2", "addoninspector2"]; + + /// <inheritdoc/> + public string DisplayName { get; init; } = "Addon Inspector v2 (Testing)"; + + /// <inheritdoc/> + public bool Ready { get; set; } + + /// <inheritdoc/> + public void Load() + { + this.addonInspector2 = new UiDebug2.UiDebug2(); + + if (this.addonInspector2 is not null) + { + this.Ready = true; + } + } + + /// <inheritdoc/> + public void Draw() + { + this.addonInspector2?.Draw(); + } +} diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonLifecycleWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonLifecycleWidget.cs index 0316cc84e..b58166e89 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonLifecycleWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonLifecycleWidget.cs @@ -1,9 +1,11 @@ +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Linq; using Dalamud.Bindings.ImGui; using Dalamud.Game.Addon.Lifecycle; -using Dalamud.Interface.Utility.Raii; -using Dalamud.Utility; +using Dalamud.Interface.Colors; +using Dalamud.Interface.Utility; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -19,7 +21,7 @@ public class AddonLifecycleWidget : IDataWindowWidget public string DisplayName { get; init; } = "Addon Lifecycle"; /// <inheritdoc/> - [MemberNotNullWhen(true, nameof(AddonLifecycle))] + [MemberNotNullWhen(true, "AddonLifecycle")] public bool Ready { get; set; } private AddonLifecycle? AddonLifecycle { get; set; } @@ -46,38 +48,97 @@ public class AddonLifecycleWidget : IDataWindowWidget return; } - foreach (var (eventType, addonListeners) in this.AddonLifecycle.EventListeners) + if (ImGui.CollapsingHeader("Listeners"u8)) { - using var eventId = ImRaii.PushId(eventType.ToString()); + ImGui.Indent(); + this.DrawEventListeners(); + ImGui.Unindent(); + } + if (ImGui.CollapsingHeader("ReceiveEvent Hooks"u8)) + { + ImGui.Indent(); + this.DrawReceiveEventHooks(); + ImGui.Unindent(); + } + } + + private void DrawEventListeners() + { + if (!this.Ready) return; + + foreach (var eventType in Enum.GetValues<AddonEvent>()) + { if (ImGui.CollapsingHeader(eventType.ToString())) { - using var eventIndent = ImRaii.PushIndent(); + ImGui.Indent(); + var listeners = this.AddonLifecycle.EventListeners.Where(listener => listener.EventType == eventType).ToList(); - if (addonListeners.Count == 0) + if (listeners.Count == 0) { - ImGui.Text("No Addons Registered for Event"u8); + ImGui.Text("No Listeners Registered for Event"u8); } - foreach (var (addonName, listeners) in addonListeners) + if (ImGui.BeginTable("AddonLifecycleListenersTable"u8, 2)) { - using var addonId = ImRaii.PushId(addonName); + ImGui.TableSetupColumn("##AddonName"u8, ImGuiTableColumnFlags.WidthFixed, 100.0f * ImGuiHelpers.GlobalScale); + ImGui.TableSetupColumn("##MethodInvoke"u8, ImGuiTableColumnFlags.WidthStretch); - if (ImGui.CollapsingHeader(addonName.IsNullOrEmpty() ? "GLOBAL" : addonName)) + foreach (var listener in listeners) { - using var addonIndent = ImRaii.PushIndent(); + ImGui.TableNextColumn(); + ImGui.Text(listener.AddonName is "" ? "GLOBAL" : listener.AddonName); - if (listeners.Count == 0) - { - ImGui.Text("No Listeners Registered for Event"u8); - } - - foreach (var listener in listeners) - { - ImGui.Text($"{listener.FunctionDelegate.Method.DeclaringType?.FullName ?? "Unknown Declaring Type"}::{listener.FunctionDelegate.Method.Name}"); - } + ImGui.TableNextColumn(); + ImGui.Text($"{listener.FunctionDelegate.Method.DeclaringType?.FullName ?? "Unknown Declaring Type"}::{listener.FunctionDelegate.Method.Name}"); } + + ImGui.EndTable(); } + + ImGui.Unindent(); + } + } + } + + private void DrawReceiveEventHooks() + { + if (!this.Ready) return; + + var listeners = this.AddonLifecycle.ReceiveEventListeners; + + if (listeners.Count == 0) + { + ImGui.Text("No ReceiveEvent Hooks are Registered"u8); + } + + foreach (var receiveEventListener in this.AddonLifecycle.ReceiveEventListeners) + { + if (ImGui.CollapsingHeader(string.Join(", ", receiveEventListener.AddonNames))) + { + ImGui.Columns(2); + + var functionAddress = receiveEventListener.FunctionAddress; + + ImGui.Text("Hook Address"u8); + ImGui.NextColumn(); + ImGui.Text($"0x{functionAddress:X} (ffxiv_dx11.exe+{functionAddress - Process.GetCurrentProcess().MainModule!.BaseAddress:X})"); + + ImGui.NextColumn(); + ImGui.Text("Hook Status"u8); + ImGui.NextColumn(); + if (receiveEventListener.Hook is null) + { + ImGui.Text("Hook is null"u8); + } + else + { + var color = receiveEventListener.Hook.IsEnabled ? ImGuiColors.HealerGreen : ImGuiColors.DalamudRed; + var text = receiveEventListener.Hook.IsEnabled ? "Enabled"u8 : "Disabled"u8; + ImGui.TextColored(color, text); + } + + ImGui.Columns(1); } } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs index 85ccd471d..c0f923fc7 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs @@ -8,7 +8,7 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// <summary> /// Widget for displaying Addon Data. /// </summary> -internal class AddonWidget : IDataWindowWidget +internal unsafe class AddonWidget : IDataWindowWidget { private string inputAddonName = string.Empty; private int inputAddonIndex; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs index 6a4f2c9ab..6f41fa46f 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs @@ -1,8 +1,7 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Bindings.ImGui; using Dalamud.Game; -using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -16,7 +15,7 @@ internal class AddressesWidget : IDataWindowWidget private nint sigResult = nint.Zero; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["address"]; + public string[]? CommandShortcuts { get; init; } = { "address" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Addresses"; @@ -47,24 +46,22 @@ internal class AddressesWidget : IDataWindowWidget } } - ImGui.AlignTextToFramePadding(); - ImGui.Text($"Result: {this.sigResult:X}"); + ImGui.Text($"Result: {this.sigResult.ToInt64():X}"); ImGui.SameLine(); - if (ImGui.Button($"C##{this.sigResult:X}")) - ImGui.SetClipboardText($"{this.sigResult:X}"); + if (ImGui.Button($"C##{this.sigResult.ToInt64():X}")) + ImGui.SetClipboardText(this.sigResult.ToInt64().ToString("X")); foreach (var debugScannedValue in BaseAddressResolver.DebugScannedValues) { ImGui.Text($"{debugScannedValue.Key}"); foreach (var valueTuple in debugScannedValue.Value) { - using var indent = ImRaii.PushIndent(10.0f); - ImGui.AlignTextToFramePadding(); - ImGui.Text($"{valueTuple.ClassName} - {Util.DescribeAddress(valueTuple.Address)}"); + ImGui.Text( + $" {valueTuple.ClassName} - {Util.DescribeAddress(valueTuple.Address)}"); ImGui.SameLine(); - if (ImGui.Button($"C##{valueTuple.Address:X}")) - ImGui.SetClipboardText($"{valueTuple.Address:X}"); + if (ImGui.Button($"C##{valueTuple.Address.ToInt64():X}")) + ImGui.SetClipboardText(valueTuple.Address.ToInt64().ToString("X")); } } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs index 68ba93b36..cc5492228 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs @@ -1,6 +1,5 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Aetherytes; -using Dalamud.Interface.Utility.Raii; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -9,13 +8,11 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// </summary> internal class AetherytesWidget : IDataWindowWidget { - private const ImGuiTableFlags TableFlags = ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders; - /// <inheritdoc/> public bool Ready { get; set; } /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["aetherytes"]; + public string[]? CommandShortcuts { get; init; } = { "aetherytes" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Aetherytes"; @@ -29,8 +26,7 @@ internal class AetherytesWidget : IDataWindowWidget /// <inheritdoc/> public void Draw() { - using var table = ImRaii.Table("##aetheryteTable"u8, 11, TableFlags); - if (!table.Success) + if (!ImGui.BeginTable("##aetheryteTable"u8, 11, ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders)) return; ImGui.TableSetupScrollFreeze(0, 1); @@ -88,5 +84,7 @@ internal class AetherytesWidget : IDataWindowWidget ImGui.TableNextColumn(); // Apartment ImGui.Text($"{info.IsApartment}"); } + + ImGui.EndTable(); } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs index 6f9bbdb1b..c3074e807 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs @@ -3,10 +3,8 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; - using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Component.GUI; - using Lumina.Text.ReadOnly; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -25,16 +23,16 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget private int selectedExtendArray; private string searchTerm = string.Empty; - private bool hideUnsetStringArrayEntries; - private bool hideUnsetExtendArrayEntries; - private bool showTextAddress; - private bool showMacroString; + private bool hideUnsetStringArrayEntries = false; + private bool hideUnsetExtendArrayEntries = false; + private bool showTextAddress = false; + private bool showMacroString = false; /// <inheritdoc/> public bool Ready { get; set; } /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["atkarray"]; + public string[]? CommandShortcuts { get; init; } = { "atkarray" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Atk Array Data"; @@ -155,14 +153,17 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget if (ImGui.IsItemHovered()) { using var tooltip = ImRaii.Tooltip(); - - var raptureAtkUnitManager = RaptureAtkUnitManager.Instance(); - for (var j = 0; j < array->SubscribedAddonsCount; j++) + if (tooltip) { - if (array->SubscribedAddons[j] == 0) - continue; + var raptureAtkUnitManager = RaptureAtkUnitManager.Instance(); - ImGui.Text(raptureAtkUnitManager->GetAddonById(array->SubscribedAddons[j])->NameString); + for (var j = 0; j < array->SubscribedAddonsCount; j++) + { + if (array->SubscribedAddons[j] == 0) + continue; + + ImGui.Text(raptureAtkUnitManager->GetAddonById(array->SubscribedAddons[j])->NameString); + } } } } @@ -241,9 +242,9 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget var atkArrayDataHolder = RaptureAtkModule.Instance()->AtkArrayDataHolder; - using (var sidebarChild = ImRaii.Child("StringArraySidebar"u8, new Vector2(300, -1), false, ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoSavedSettings)) + using (var sidebarchild = ImRaii.Child("StringArraySidebar"u8, new Vector2(300, -1), false, ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoSavedSettings)) { - if (sidebarChild) + if (sidebarchild) { ImGui.SetNextItemWidth(-1); ImGui.InputTextWithHint("##TextSearch"u8, "Search..."u8, ref this.searchTerm, 256, ImGuiInputTextFlags.AutoSelectAll); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs index bbef5fee3..06dc1b11e 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs @@ -15,7 +15,7 @@ internal class BuddyListWidget : IDataWindowWidget public bool Ready { get; set; } /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["buddy", "buddylist"]; + public string[]? CommandShortcuts { get; init; } = { "buddy", "buddylist" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Buddy List"; @@ -32,65 +32,18 @@ internal class BuddyListWidget : IDataWindowWidget var buddyList = Service<BuddyList>.Get(); ImGui.Checkbox("Resolve GameData"u8, ref this.resolveGameData); - - var companionBuddy = buddyList.CompanionBuddy; - if (companionBuddy == null) { - ImGui.Text("[Companion] null"u8); - } - else - { - ImGui.Text($"[Companion] {companionBuddy.Address:X} - {companionBuddy.EntityId} - {companionBuddy.DataID}"); - if (this.resolveGameData) + var member = buddyList.CompanionBuddy; + if (member == null) { - var gameObject = companionBuddy.GameObject; - if (gameObject == null) - { - ImGui.Text("GameObject was null"u8); - } - else - { - Util.PrintGameObject(gameObject, "-", this.resolveGameData); - } + ImGui.Text("[Companion] null"u8); } - } - - var petBuddy = buddyList.PetBuddy; - if (petBuddy == null) - { - ImGui.Text("[Pet] null"u8); - } - else - { - ImGui.Text($"[Pet] {petBuddy.Address:X} - {petBuddy.EntityId} - {petBuddy.DataID}"); - if (this.resolveGameData) + else { - var gameObject = petBuddy.GameObject; - if (gameObject == null) - { - ImGui.Text("GameObject was null"u8); - } - else - { - Util.PrintGameObject(gameObject, "-", this.resolveGameData); - } - } - } - - var count = buddyList.Length; - if (count == 0) - { - ImGui.Text("[BattleBuddy] None present"u8); - } - else - { - for (var i = 0; i < count; i++) - { - var member = buddyList[i]; - ImGui.Text($"[BattleBuddy] [{i}] {member?.Address ?? 0:X} - {member?.EntityId ?? 0} - {member?.DataID ?? 0}"); + ImGui.Text($"[Companion] {member.Address.ToInt64():X} - {member.EntityId} - {member.DataID}"); if (this.resolveGameData) { - var gameObject = member?.GameObject; + var gameObject = member.GameObject; if (gameObject == null) { ImGui.Text("GameObject was null"u8); @@ -102,5 +55,57 @@ internal class BuddyListWidget : IDataWindowWidget } } } + + { + var member = buddyList.PetBuddy; + if (member == null) + { + ImGui.Text("[Pet] null"u8); + } + else + { + ImGui.Text($"[Pet] {member.Address.ToInt64():X} - {member.EntityId} - {member.DataID}"); + if (this.resolveGameData) + { + var gameObject = member.GameObject; + if (gameObject == null) + { + ImGui.Text("GameObject was null"u8); + } + else + { + Util.PrintGameObject(gameObject, "-", this.resolveGameData); + } + } + } + } + + { + var count = buddyList.Length; + if (count == 0) + { + ImGui.Text("[BattleBuddy] None present"u8); + } + else + { + for (var i = 0; i < count; i++) + { + var member = buddyList[i]; + ImGui.Text($"[BattleBuddy] [{i}] {member?.Address.ToInt64():X} - {member?.EntityId} - {member?.DataID}"); + if (this.resolveGameData) + { + var gameObject = member?.GameObject; + if (gameObject == null) + { + ImGui.Text("GameObject was null"u8); + } + else + { + Util.PrintGameObject(gameObject, "-", this.resolveGameData); + } + } + } + } + } } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs index e1b06aaf3..f5a521672 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs @@ -2,6 +2,7 @@ using System.Linq; using Dalamud.Bindings.ImGui; using Dalamud.Game.Command; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -11,12 +12,8 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// </summary> internal class CommandWidget : IDataWindowWidget { - private const ImGuiTableFlags TableFlags = ImGuiTableFlags.ScrollY | ImGuiTableFlags.Borders | - ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.Sortable | - ImGuiTableFlags.SortTristate; - /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["command"]; + public string[]? CommandShortcuts { get; init; } = { "command" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Command"; @@ -35,7 +32,9 @@ internal class CommandWidget : IDataWindowWidget { var commandManager = Service<CommandManager>.Get(); - using var table = ImRaii.Table("CommandList"u8, 4, TableFlags); + var tableFlags = ImGuiTableFlags.ScrollY | ImGuiTableFlags.Borders | ImGuiTableFlags.SizingStretchProp | + ImGuiTableFlags.Sortable | ImGuiTableFlags.SortTristate; + using var table = ImRaii.Table("CommandList"u8, 4, tableFlags); if (table) { ImGui.TableSetupScrollFreeze(0, 1); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs index e3a737a25..6ecee48ed 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs @@ -1,4 +1,4 @@ -using Dalamud.Bindings.ImGui; +using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Conditions; using Dalamud.Utility; @@ -13,7 +13,7 @@ internal class ConditionWidget : IDataWindowWidget public bool Ready { get; set; } /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["condition"]; + public string[]? CommandShortcuts { get; init; } = { "condition" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Condition"; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ConfigurationWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ConfigurationWidget.cs index df5aeab56..f66b50fca 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ConfigurationWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ConfigurationWidget.cs @@ -1,4 +1,4 @@ -using Dalamud.Configuration.Internal; +using Dalamud.Configuration.Internal; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -9,10 +9,10 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; internal class ConfigurationWidget : IDataWindowWidget { /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["config", "configuration"]; - + public string[]? CommandShortcuts { get; init; } = { "config", "configuration" }; + /// <inheritdoc/> - public string DisplayName { get; init; } = "Configuration"; + public string DisplayName { get; init; } = "Configuration"; /// <inheritdoc/> public bool Ready { get; set; } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs index ebb5b6581..83db4ac6e 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Numerics; using System.Reflection; @@ -10,7 +11,6 @@ using Dalamud.Interface.ImGuiNotification.Internal; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin.Ipc.Internal; - using Newtonsoft.Json; using Formatting = Newtonsoft.Json.Formatting; @@ -20,17 +20,21 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// <summary> /// Widget for displaying plugin data share modules. /// </summary> +[SuppressMessage( + "StyleCop.CSharp.LayoutRules", + "SA1519:Braces should not be omitted from multi-line child statement", + Justification = "Multiple fixed blocks")] internal class DataShareWidget : IDataWindowWidget { - private const ImGuiTabItemFlags NoCloseButton = (ImGuiTabItemFlags)ImGuiTabItemFlagsPrivate.NoCloseButton; + private const ImGuiTabItemFlags NoCloseButton = (ImGuiTabItemFlags)(1 << 20); - private readonly List<(string Name, byte[]? Data)> dataView = []; + private readonly List<(string Name, byte[]? Data)> dataView = new(); private int nextTab = -1; private IReadOnlyDictionary<string, CallGateChannel>? gates; private List<CallGateChannel>? gatesSorted; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["datashare"]; + public string[]? CommandShortcuts { get; init; } = { "datashare" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Data Share & Call Gate"; @@ -45,37 +49,42 @@ internal class DataShareWidget : IDataWindowWidget } /// <inheritdoc/> - public void Draw() + public unsafe void Draw() { using var tabbar = ImRaii.TabBar("##tabbar"u8); if (!tabbar.Success) return; var d = true; - using (var tabItem = ImRaii.TabItem("Data Share##tabbar-datashare"u8, ref d, NoCloseButton | (this.nextTab == 0 ? ImGuiTabItemFlags.SetSelected : 0))) + using (var tabitem = ImRaii.TabItem( + "Data Share##tabbar-datashare"u8, + ref d, + NoCloseButton | (this.nextTab == 0 ? ImGuiTabItemFlags.SetSelected : 0))) { - if (tabItem.Success) + if (tabitem.Success) this.DrawDataShare(); } - using (var tabItem = ImRaii.TabItem("Call Gate##tabbar-callgate"u8, ref d, NoCloseButton | (this.nextTab == 1 ? ImGuiTabItemFlags.SetSelected : 0))) + using (var tabitem = ImRaii.TabItem( + "Call Gate##tabbar-callgate"u8, + ref d, + NoCloseButton | (this.nextTab == 1 ? ImGuiTabItemFlags.SetSelected : 0))) { - if (tabItem.Success) + if (tabitem.Success) this.DrawCallGate(); } for (var i = 0; i < this.dataView.Count; i++) { using var idpush = ImRaii.PushId($"##tabbar-data-{i}"); - var (name, data) = this.dataView[i]; d = true; - - using var tabitem = ImRaii.TabItem(name, ref d, this.nextTab == 2 + i ? ImGuiTabItemFlags.SetSelected : 0); - + using var tabitem = ImRaii.TabItem( + name, + ref d, + this.nextTab == 2 + i ? ImGuiTabItemFlags.SetSelected : 0); if (!d) this.dataView.RemoveAt(i--); - if (!tabitem.Success) continue; @@ -87,7 +96,7 @@ internal class DataShareWidget : IDataWindowWidget try { var dataShare = Service<DataShare>.Get(); - var data2 = dataShare.GetData<object>(name, new DataCachePluginId("DataShareWidget", Guid.Empty)); + var data2 = dataShare.GetData<object>(name); try { data = Encoding.UTF8.GetBytes( @@ -98,7 +107,7 @@ internal class DataShareWidget : IDataWindowWidget } finally { - dataShare.RelinquishData(name, new DataCachePluginId("DataShareWidget", Guid.Empty)); + dataShare.RelinquishData(name); } } catch (Exception e) @@ -113,7 +122,11 @@ internal class DataShareWidget : IDataWindowWidget if (ImGui.Button("Copy"u8)) ImGui.SetClipboardText(data); - ImGui.InputTextMultiline("text"u8, data, ImGui.GetContentRegionAvail(), ImGuiInputTextFlags.ReadOnly); + ImGui.InputTextMultiline( + "text"u8, + data, + ImGui.GetContentRegionAvail(), + ImGuiInputTextFlags.ReadOnly); } this.nextTab = -1; @@ -128,10 +141,8 @@ internal class DataShareWidget : IDataWindowWidget sb.Append(ReprType(mi.DeclaringType)) .Append("::") .Append(mi.Name); - if (!withParams) return sb.ToString(); - sb.Append('('); var parfirst = true; foreach (var par in mi.GetParameters()) @@ -140,7 +151,6 @@ internal class DataShareWidget : IDataWindowWidget sb.Append(", "); else parfirst = false; - sb.AppendLine() .Append('\t') .Append(ReprType(par.ParameterType)) @@ -150,11 +160,9 @@ internal class DataShareWidget : IDataWindowWidget if (!parfirst) sb.AppendLine(); - sb.Append(')'); if (mi.ReturnType != typeof(void)) sb.Append(" -> ").Append(ReprType(mi.ReturnType)); - return sb.ToString(); static string WithoutGeneric(string s) @@ -163,7 +171,8 @@ internal class DataShareWidget : IDataWindowWidget return i != -1 ? s[..i] : s; } - static string ReprType(Type? t) => t switch + static string ReprType(Type? t) => + t switch { null => "null", _ when t == typeof(string) => "string", @@ -205,19 +214,18 @@ internal class DataShareWidget : IDataWindowWidget var offset = ImGui.GetCursorScreenPos() + new Vector2(0, framepad ? ImGui.GetStyle().FramePadding.Y : 0); if (framepad) ImGui.AlignTextToFramePadding(); - ImGui.Text(s); if (ImGui.IsItemHovered()) { ImGui.SetNextWindowPos(offset - ImGui.GetStyle().WindowPadding); var vp = ImGui.GetWindowViewport(); var wrx = (vp.WorkPos.X + vp.WorkSize.X) - offset.X; - ImGui.SetNextWindowSizeConstraints(Vector2.One, new(wrx, float.MaxValue)); using (ImRaii.Tooltip()) { - using var pushedWrap = ImRaii.TextWrapPos(wrx); + ImGui.PushTextWrapPos(wrx); ImGui.TextWrapped(tooltip?.Invoke() ?? s); + ImGui.PopTextWrapPos(); } } @@ -238,9 +246,6 @@ internal class DataShareWidget : IDataWindowWidget callGate.PurgeEmptyGates(); using var table = ImRaii.Table("##callgate-table"u8, 5); - if (!table.Success) - return; - ImGui.TableSetupColumn("Name"u8, ImGuiTableColumnFlags.DefaultSort); ImGui.TableSetupColumn("Action"u8); ImGui.TableSetupColumn("Func"u8); @@ -262,8 +267,12 @@ internal class DataShareWidget : IDataWindowWidget { ImGui.TableNextRow(); this.DrawTextCell(item.Name); - this.DrawTextCell(ReprMethod(item.Action?.Method, false), () => ReprMethod(item.Action?.Method, true)); - this.DrawTextCell(ReprMethod(item.Func?.Method, false), () => ReprMethod(item.Func?.Method, true)); + this.DrawTextCell( + ReprMethod(item.Action?.Method, false), + () => ReprMethod(item.Action?.Method, true)); + this.DrawTextCell( + ReprMethod(item.Func?.Method, false), + () => ReprMethod(item.Func?.Method, true)); if (subs.Count == 0) { this.DrawTextCell("0"); @@ -278,43 +287,47 @@ internal class DataShareWidget : IDataWindowWidget private void DrawDataShare() { - using var table = ImRaii.Table("###DataShareTable"u8, 5, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg); - if (!table.Success) + if (!ImGui.BeginTable("###DataShareTable"u8, 5, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg)) return; - ImGui.TableSetupColumn("Shared Tag"u8); - ImGui.TableSetupColumn("Show"u8); - ImGui.TableSetupColumn("Creator"u8); - ImGui.TableSetupColumn("#"u8, ImGuiTableColumnFlags.WidthFixed, 30 * ImGuiHelpers.GlobalScale); - ImGui.TableSetupColumn("Consumers"u8); - ImGui.TableHeadersRow(); - - foreach (var share in Service<DataShare>.Get().GetAllShares()) + try { - ImGui.TableNextRow(); - this.DrawTextCell(share.Tag, null, true); - - ImGui.TableNextColumn(); - if (ImGui.Button($"Show##datasharetable-show-{share.Tag}")) + ImGui.TableSetupColumn("Shared Tag"u8); + ImGui.TableSetupColumn("Show"u8); + ImGui.TableSetupColumn("Creator Assembly"u8); + ImGui.TableSetupColumn("#"u8, ImGuiTableColumnFlags.WidthFixed, 30 * ImGuiHelpers.GlobalScale); + ImGui.TableSetupColumn("Consumers"u8); + ImGui.TableHeadersRow(); + foreach (var share in Service<DataShare>.Get().GetAllShares()) { - var index = 0; - for (; index < this.dataView.Count; index++) + ImGui.TableNextRow(); + this.DrawTextCell(share.Tag, null, true); + + ImGui.TableNextColumn(); + if (ImGui.Button($"Show##datasharetable-show-{share.Tag}")) { - if (this.dataView[index].Name == share.Tag) - break; + var index = 0; + for (; index < this.dataView.Count; index++) + { + if (this.dataView[index].Name == share.Tag) + break; + } + + if (index == this.dataView.Count) + this.dataView.Add((share.Tag, null)); + else + this.dataView[index] = (share.Tag, null); + this.nextTab = 2 + index; } - if (index == this.dataView.Count) - this.dataView.Add((share.Tag, null)); - else - this.dataView[index] = (share.Tag, null); - - this.nextTab = 2 + index; + this.DrawTextCell(share.CreatorAssembly, null, true); + this.DrawTextCell(share.Users.Length.ToString(), null, true); + this.DrawTextCell(string.Join(", ", share.Users), null, true); } - - this.DrawTextCell(share.CreatorPluginId.InternalName, () => share.CreatorPluginId.EffectiveWorkingId.ToString(), true); - this.DrawTextCell(share.UserPluginIds.Length.ToString(), null, true); - this.DrawTextCell(string.Join(", ", share.UserPluginIds.Select(c => c.InternalName)), () => string.Join("\n", share.UserPluginIds.Select(c => $"{c.InternalName} ({c.EffectiveWorkingId.ToString()}")), true); + } + finally + { + ImGui.EndTable(); } } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs index 838f11632..0a40c9be7 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Threading; using Dalamud.Bindings.ImGui; @@ -21,7 +21,7 @@ internal class DtrBarWidget : IDataWindowWidget, IDisposable private CancellationTokenSource? loadTestThreadCt; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["dtr", "dtrbar"]; + public string[]? CommandShortcuts { get; init; } = { "dtr", "dtrbar" }; /// <inheritdoc/> public string DisplayName { get; init; } = "DTR Bar"; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs index b2f4bf70f..50ed79b3d 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs @@ -1,6 +1,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Fates; using Dalamud.Interface.Textures.Internal; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -10,11 +11,8 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// </summary> internal class FateTableWidget : IDataWindowWidget { - private const ImGuiTableFlags TableFlags = ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | - ImGuiTableFlags.Borders | ImGuiTableFlags.NoSavedSettings; - /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["fate", "fatetable"]; + public string[]? CommandShortcuts { get; init; } = { "fate", "fatetable" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Fate Table"; @@ -40,7 +38,7 @@ internal class FateTableWidget : IDataWindowWidget return; } - using var table = ImRaii.Table("FateTable"u8, 13, TableFlags); + using var table = ImRaii.Table("FateTable"u8, 13, ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders | ImGuiTableFlags.NoSavedSettings); if (!table) return; ImGui.TableSetupColumn("Index"u8, ImGuiTableColumnFlags.WidthFixed, 40); @@ -100,11 +98,11 @@ internal class FateTableWidget : IDataWindowWidget if (ImGui.IsItemHovered()) { ImGui.SetMouseCursor(ImGuiMouseCursor.Hand); - - using var tooltip = ImRaii.Tooltip(); + ImGui.BeginTooltip(); ImGui.Text("Click to copy IconId"u8); ImGui.Text($"ID: {fate.IconId} – Size: {texture.Width}x{texture.Height}"); ImGui.Image(texture.Handle, new(texture.Width, texture.Height)); + ImGui.EndTooltip(); } if (ImGui.IsItemClicked()) @@ -125,11 +123,11 @@ internal class FateTableWidget : IDataWindowWidget if (ImGui.IsItemHovered()) { ImGui.SetMouseCursor(ImGuiMouseCursor.Hand); - - using var tooltip = ImRaii.Tooltip(); + ImGui.BeginTooltip(); ImGui.Text("Click to copy MapIconId"u8); ImGui.Text($"ID: {fate.MapIconId} – Size: {texture.Width}x{texture.Height}"); ImGui.Image(texture.Handle, new(texture.Width, texture.Height)); + ImGui.EndTooltip(); } if (ImGui.IsItemClicked()) diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/FlyTextWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/FlyTextWidget.cs index 00e424551..d20aa5cb1 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/FlyTextWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/FlyTextWidget.cs @@ -3,7 +3,6 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui.FlyText; -using Dalamud.Interface.Utility.Raii; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -23,7 +22,7 @@ internal class FlyTextWidget : IDataWindowWidget private Vector4 flyColor = new(1, 0, 0, 1); /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["flytext"]; + public string[]? CommandShortcuts { get; init; } = { "flytext" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Fly Text"; @@ -40,18 +39,18 @@ internal class FlyTextWidget : IDataWindowWidget /// <inheritdoc/> public void Draw() { - using (var combo = ImRaii.Combo("Kind"u8, $"{this.flyKind} ({(int)this.flyKind})")) + if (ImGui.BeginCombo("Kind"u8, $"{this.flyKind} ({(int)this.flyKind})")) { - if (combo.Success) + var values = Enum.GetValues<FlyTextKind>().Distinct(); + foreach (var value in values) { - foreach (var value in Enum.GetValues<FlyTextKind>().Distinct()) + if (ImGui.Selectable($"{value} ({(int)value})")) { - if (ImGui.Selectable($"{value} ({(int)value})")) - { - this.flyKind = value; - } + this.flyKind = value; } } + + ImGui.EndCombo(); } ImGui.InputText("Text1"u8, ref this.flyText1, 200); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs index c19fed848..91f1af98e 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs @@ -1,17 +1,9 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Numerics; -using System.Threading.Tasks; using Dalamud.Bindings.ImGui; -using Dalamud.Interface.Components; -using Dalamud.Interface.ImGuiSeStringRenderer; -using Dalamud.Interface.Textures.Internal; using Dalamud.Interface.Utility; -using Dalamud.Interface.Utility.Internal; -using Dalamud.Interface.Utility.Raii; - -using Lumina.Text.ReadOnly; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -20,18 +12,16 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// </summary> internal class FontAwesomeTestWidget : IDataWindowWidget { - private static readonly string[] First = ["(Show All)", "(Undefined)"]; - private List<FontAwesomeIcon>? icons; private List<string>? iconNames; private string[]? iconCategories; private int selectedIconCategory; private string iconSearchInput = string.Empty; private bool iconSearchChanged = true; - private bool useFixedWidth; + private bool useFixedWidth = false; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["fa", "fatest", "fontawesome"]; + public string[]? CommandShortcuts { get; init; } = { "fa", "fatest", "fontawesome" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Font Awesome Test"; @@ -48,9 +38,11 @@ internal class FontAwesomeTestWidget : IDataWindowWidget /// <inheritdoc/> public void Draw() { - using var pushedStyle = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero); + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero); - this.iconCategories ??= First.Concat(FontAwesomeHelpers.GetCategories().Skip(1)).ToArray(); + this.iconCategories ??= new[] { "(Show All)", "(Undefined)" } + .Concat(FontAwesomeHelpers.GetCategories().Skip(1)) + .ToArray(); if (this.iconSearchChanged) { @@ -95,32 +87,16 @@ internal class FontAwesomeTestWidget : IDataWindowWidget ImGuiHelpers.ScaledDummy(10f); for (var i = 0; i < this.icons?.Count; i++) { - if (this.icons[i] == FontAwesomeIcon.None) - continue; - - ImGui.AlignTextToFramePadding(); ImGui.Text($"0x{(int)this.icons[i].ToIconChar():X}"); ImGuiHelpers.ScaledRelativeSameLine(50f); ImGui.Text($"{this.iconNames?[i]}"); ImGuiHelpers.ScaledRelativeSameLine(280f); - - using var pushedFont = ImRaii.PushFont(this.useFixedWidth ? InterfaceManager.IconFontFixedWidth : InterfaceManager.IconFont); + ImGui.PushFont(this.useFixedWidth ? InterfaceManager.IconFontFixedWidth : InterfaceManager.IconFont); ImGui.Text(this.icons[i].ToIconString()); - ImGuiHelpers.ScaledRelativeSameLine(320f); - if (this.useFixedWidth - ? ImGui.Button($"{(char)this.icons[i]}##FontAwesomeIconButton{i}") - : ImGuiComponents.IconButton($"##FontAwesomeIconButton{i}", this.icons[i])) - { - _ = Service<DevTextureSaveMenu>.Get().ShowTextureSaveMenuAsync( - this.DisplayName, - this.icons[i].ToString(), - Task.FromResult( - Service<TextureManager>.Get().CreateTextureFromSeString( - ReadOnlySeString.FromText(this.icons[i].ToIconString()), - new SeStringDrawParams { Font = ImGui.GetFont(), FontSize = ImGui.GetFontSize(), ScreenOffset = Vector2.Zero }))); - } - + ImGui.PopFont(); ImGuiHelpers.ScaledDummy(2f); } + + ImGui.PopStyleVar(); } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/GamePrebakedFontsTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/GamePrebakedFontsTestWidget.cs index 0c0289bd0..c653e9185 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/GamePrebakedFontsTestWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/GamePrebakedFontsTestWidget.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Numerics; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; @@ -13,9 +14,7 @@ using Dalamud.Interface.ImGuiFontChooserDialog; using Dalamud.Interface.ManagedFontAtlas; using Dalamud.Interface.ManagedFontAtlas.Internals; using Dalamud.Interface.Utility; -using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; - using Serilog; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -26,11 +25,11 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable { private static readonly string[] FontScaleModes = - [ + { nameof(FontScaleMode.Default), nameof(FontScaleMode.SkipHandling), nameof(FontScaleMode.UndoGlobalScale), - ]; + }; private ImVectorWrapper<byte> testStringBuffer; private IFontAtlas? privateAtlas; @@ -249,8 +248,7 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable { ImGui.Text($"{gfs.SizePt}pt"); ImGui.SameLine(offsetX); - - using var pushedWrap = ImRaii.TextWrapPos(this.useWordWrap ? 0f : -1f); + ImGui.PushTextWrapPos(this.useWordWrap ? 0f : -1f); try { if (handle.Value.LoadException is { } exc) @@ -265,7 +263,6 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable { if (!this.atlasScaleMode) ImGui.SetWindowFontScale(1 / ImGuiHelpers.GlobalScale); - if (counter++ % 2 == 0) { using var pushPop = handle.Value.Push(); @@ -282,6 +279,7 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable finally { ImGui.SetWindowFontScale(1); + ImGui.PopTextWrapPos(); } } } @@ -340,7 +338,7 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable return; - static void TestSingle(ImFontPtr fontPtr, IFontHandle handle) + void TestSingle(ImFontPtr fontPtr, IFontHandle handle) { var dim = ImGui.CalcTextSizeA(fontPtr, fontPtr.FontSize, float.MaxValue, 0f, "Test string"u8, out _); Log.Information($"{nameof(GamePrebakedFontsTestWidget)}: {handle} => {dim}"); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs index 12aa4c4ae..65e6cd3d6 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs @@ -1,4 +1,4 @@ -using Dalamud.Bindings.ImGui; +using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.GamePad; using Dalamud.Utility; @@ -10,7 +10,7 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; internal class GamepadWidget : IDataWindowWidget { /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["gamepad", "controller"]; + public string[]? CommandShortcuts { get; init; } = { "gamepad", "controller" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Gamepad"; @@ -39,10 +39,22 @@ internal class GamepadWidget : IDataWindowWidget ImGui.SetClipboardText($"{Util.DescribeAddress(gamepadState.GamepadInputAddress)}"); #endif - this.DrawHelper("Buttons Raw", (uint)gamepadState.ButtonsRaw, gamepadState.Raw); - this.DrawHelper("Buttons Pressed", (uint)gamepadState.ButtonsPressed, gamepadState.Pressed); - this.DrawHelper("Buttons Repeat", (uint)gamepadState.ButtonsRepeat, gamepadState.Repeat); - this.DrawHelper("Buttons Released", (uint)gamepadState.ButtonsReleased, gamepadState.Released); + this.DrawHelper( + "Buttons Raw", + (uint)gamepadState.ButtonsRaw, + gamepadState.Raw); + this.DrawHelper( + "Buttons Pressed", + (uint)gamepadState.ButtonsPressed, + gamepadState.Pressed); + this.DrawHelper( + "Buttons Repeat", + (uint)gamepadState.ButtonsRepeat, + gamepadState.Repeat); + this.DrawHelper( + "Buttons Released", + (uint)gamepadState.ButtonsReleased, + gamepadState.Released); ImGui.Text($"LeftStick {gamepadState.LeftStick}"); ImGui.Text($"RightStick {gamepadState.RightStick}"); } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs index 8badcfaf8..7a5a9c89b 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs @@ -12,7 +12,7 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; internal class GaugeWidget : IDataWindowWidget { /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["gauge", "jobgauge", "job"]; + public string[]? CommandShortcuts { get; init; } = { "gauge", "jobgauge", "job" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Job Gauge"; @@ -39,7 +39,8 @@ internal class GaugeWidget : IDataWindowWidget return; } - JobGaugeBase? gauge = player.ClassJob.RowId switch + var jobID = player.ClassJob.RowId; + JobGaugeBase? gauge = jobID switch { 19 => jobGauges.Get<PLDGauge>(), 20 => jobGauges.Get<MNKGauge>(), diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/HookWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/HookWidget.cs index e19281b40..3ad8f86c2 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/HookWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/HookWidget.cs @@ -1,16 +1,14 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Dalamud.Bindings.ImGui; using Dalamud.Game; +using Dalamud.Game.Addon.Lifecycle; using Dalamud.Hooking; -using Dalamud.Interface.Utility.Raii; using FFXIVClientStructs.FFXIV.Component.GUI; - using Serilog; - using Windows.Win32.Foundation; using Windows.Win32.UI.WindowsAndMessaging; @@ -26,17 +24,17 @@ internal unsafe class HookWidget : IDataWindowWidget private Hook<MessageBoxWDelegate>? messageBoxMinHook; private bool hookUseMinHook; - private int hookStressTestCount; + private int hookStressTestCount = 0; private int hookStressTestMax = 1000; private int hookStressTestWait = 100; private int hookStressTestMaxDegreeOfParallelism = 10; private StressTestHookTarget hookStressTestHookTarget = StressTestHookTarget.Random; - private bool hookStressTestRunning; + private bool hookStressTestRunning = false; private MessageBoxWDelegate? messageBoxWOriginal; private AddonFinalizeDelegate? addonFinalizeOriginal; - private nint address; + private AddonLifecycleAddressResolver? address; private delegate int MessageBoxWDelegate( IntPtr hWnd, @@ -57,7 +55,7 @@ internal unsafe class HookWidget : IDataWindowWidget public string DisplayName { get; init; } = "Hook"; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["hook"]; + public string[]? CommandShortcuts { get; init; } = { "hook" }; /// <inheritdoc/> public bool Ready { get; set; } @@ -67,8 +65,8 @@ internal unsafe class HookWidget : IDataWindowWidget { this.Ready = true; - var sigScanner = Service<TargetSigScanner>.Get(); - this.address = sigScanner.ScanText("E8 ?? ?? ?? ?? 48 83 EF 01 75 D5"); + this.address = new AddonLifecycleAddressResolver(); + this.address.Setup(Service<TargetSigScanner>.Get()); } /// <inheritdoc/> @@ -106,68 +104,67 @@ internal unsafe class HookWidget : IDataWindowWidget ImGui.Separator(); - using (ImRaii.Disabled(this.hookStressTestRunning)) + ImGui.BeginDisabled(this.hookStressTestRunning); + ImGui.Text("Stress Test"u8); + + if (ImGui.InputInt("Max"u8, ref this.hookStressTestMax)) + this.hookStressTestCount = 0; + + ImGui.InputInt("Wait (ms)"u8, ref this.hookStressTestWait); + ImGui.InputInt("Max Degree of Parallelism"u8, ref this.hookStressTestMaxDegreeOfParallelism); + + if (ImGui.BeginCombo("Target"u8, HookTargetToString(this.hookStressTestHookTarget))) { - ImGui.Text("Stress Test"u8); - - if (ImGui.InputInt("Max"u8, ref this.hookStressTestMax)) - this.hookStressTestCount = 0; - - ImGui.InputInt("Wait (ms)"u8, ref this.hookStressTestWait); - ImGui.InputInt("Max Degree of Parallelism"u8, ref this.hookStressTestMaxDegreeOfParallelism); - - using (var combo = ImRaii.Combo("Target"u8, HookTargetToString(this.hookStressTestHookTarget))) + foreach (var target in Enum.GetValues<StressTestHookTarget>()) { - if (combo.Success) - { - foreach (var target in Enum.GetValues<StressTestHookTarget>()) - { - if (ImGui.Selectable(HookTargetToString(target), this.hookStressTestHookTarget == target)) - this.hookStressTestHookTarget = target; - } - } + if (ImGui.Selectable(HookTargetToString(target), this.hookStressTestHookTarget == target)) + this.hookStressTestHookTarget = target; } - if (ImGui.Button("Stress Test"u8)) - { - Task.Run(() => - { - this.hookStressTestRunning = true; - this.hookStressTestCount = 0; - Parallel.For( - 0, - this.hookStressTestMax, - new ParallelOptions - { - MaxDegreeOfParallelism = this.hookStressTestMaxDegreeOfParallelism, - }, - _ => - { - this.hookStressTestList.Add(this.HookTarget(this.hookStressTestHookTarget)); - this.hookStressTestCount++; - Thread.Sleep(this.hookStressTestWait); - }); - }).ContinueWith(t => - { - if (t.IsFaulted) - { - Log.Error(t.Exception, "Stress test failed"); - } - else - { - Log.Information("Stress test completed"); - } - - this.hookStressTestRunning = false; - this.hookStressTestList.ForEach(hook => - { - hook.Dispose(); - }); - this.hookStressTestList.Clear(); - }); - } + ImGui.EndCombo(); } + if (ImGui.Button("Stress Test"u8)) + { + Task.Run(() => + { + this.hookStressTestRunning = true; + this.hookStressTestCount = 0; + Parallel.For( + 0, + this.hookStressTestMax, + new ParallelOptions + { + MaxDegreeOfParallelism = this.hookStressTestMaxDegreeOfParallelism, + }, + _ => + { + this.hookStressTestList.Add(this.HookTarget(this.hookStressTestHookTarget)); + this.hookStressTestCount++; + Thread.Sleep(this.hookStressTestWait); + }); + }).ContinueWith(t => + { + if (t.IsFaulted) + { + Log.Error(t.Exception, "Stress test failed"); + } + else + { + Log.Information("Stress test completed"); + } + + this.hookStressTestRunning = false; + this.hookStressTestList.ForEach(hook => + { + hook.Dispose(); + }); + this.hookStressTestList.Clear(); + }); + } + + ImGui.EndDisabled(); + ImGui.Text("Status: " + (this.hookStressTestRunning ? "Running" : "Idle")); ImGui.ProgressBar(this.hookStressTestCount / (float)this.hookStressTestMax, new System.Numerics.Vector2(0, 0), $"{this.hookStressTestCount}/{this.hookStressTestMax}"); } @@ -207,6 +204,11 @@ internal unsafe class HookWidget : IDataWindowWidget this.addonFinalizeOriginal!(unitManager, atkUnitBase); } + private void OnAddonUpdate(AtkUnitBase* thisPtr, float delta) + { + Log.Information("OnAddonUpdate"); + } + private IDalamudHook HookMessageBoxW() { var hook = Hook<MessageBoxWDelegate>.FromSymbol( @@ -222,7 +224,7 @@ internal unsafe class HookWidget : IDataWindowWidget private IDalamudHook HookAddonFinalize() { - var hook = Hook<AddonFinalizeDelegate>.FromAddress(this.address, this.OnAddonFinalize); + var hook = Hook<AddonFinalizeDelegate>.FromAddress(this.address!.AddonFinalize, this.OnAddonFinalize); this.addonFinalizeOriginal = hook.Original; hook.Enable(); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs index 670b83f67..0395f4c96 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs @@ -8,7 +8,6 @@ using Dalamud.Interface.Textures.Internal; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Internal; -using Dalamud.Interface.Utility.Raii; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -34,7 +33,7 @@ public class IconBrowserWidget : IDataWindowWidget private Vector2 lastWindowSize = Vector2.Zero; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["icon", "icons"]; + public string[]? CommandShortcuts { get; init; } = { "icon", "icons" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Icon Browser"; @@ -63,7 +62,6 @@ public class IconBrowserWidget : IDataWindowWidget // continue; if (!texm.TryGetIconPath(new((uint)iconId), out var path)) continue; - result.Add((iconId, path)); } @@ -84,17 +82,17 @@ public class IconBrowserWidget : IDataWindowWidget { this.RecalculateIndexRange(); - using (var child = ImRaii.Child("ScrollableSection"u8, ImGui.GetContentRegionAvail(), false, ImGuiWindowFlags.NoMove)) + if (ImGui.BeginChild("ScrollableSection"u8, ImGui.GetContentRegionAvail(), false, ImGuiWindowFlags.NoMove)) { - if (child.Success) - { - var itemsPerRow = (int)MathF.Floor(ImGui.GetContentRegionMax().X / (this.iconSize.X + ImGui.GetStyle().ItemSpacing.X)); - var itemHeight = this.iconSize.Y + ImGui.GetStyle().ItemSpacing.Y; + var itemsPerRow = (int)MathF.Floor( + ImGui.GetContentRegionMax().X / (this.iconSize.X + ImGui.GetStyle().ItemSpacing.X)); + var itemHeight = this.iconSize.Y + ImGui.GetStyle().ItemSpacing.Y; - ImGuiClip.ClippedDraw(this.valueRange!, this.DrawIcon, itemsPerRow, itemHeight); - } + ImGuiClip.ClippedDraw(this.valueRange!, this.DrawIcon, itemsPerRow, itemHeight); } + ImGui.EndChild(); + this.ProcessMouseDragging(); } } @@ -120,16 +118,16 @@ public class IconBrowserWidget : IDataWindowWidget { ImGui.Columns(2); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - if (ImGui.InputInt("##StartRange"u8, ref this.startRange)) + ImGui.PushItemWidth(ImGui.GetContentRegionAvail().X); + if (ImGui.InputInt("##StartRange"u8, ref this.startRange, 0, 0)) { this.startRange = Math.Clamp(this.startRange, 0, MaxIconId); this.valueRange = null; } ImGui.NextColumn(); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - if (ImGui.InputInt("##StopRange"u8, ref this.stopRange)) + ImGui.PushItemWidth(ImGui.GetContentRegionAvail().X); + if (ImGui.InputInt("##StopRange"u8, ref this.stopRange, 0, 0)) { this.stopRange = Math.Clamp(this.stopRange, 0, MaxIconId); this.valueRange = null; @@ -153,10 +151,6 @@ public class IconBrowserWidget : IDataWindowWidget var texm = Service<TextureManager>.Get(); var cursor = ImGui.GetCursorScreenPos(); - var white = ImGui.GetColorU32(ImGuiColors.DalamudWhite); - var red = ImGui.GetColorU32(ImGuiColors.DalamudRed); - var drawList = ImGui.GetWindowDrawList(); - if (texm.Shared.GetFromGameIcon(iconId).TryGetWrap(out var texture, out var exc)) { ImGui.Image(texture.Handle, this.iconSize); @@ -164,17 +158,21 @@ public class IconBrowserWidget : IDataWindowWidget // If we have the option to show a tooltip image, draw the image, but make sure it's not too big. if (ImGui.IsItemHovered() && this.showTooltipImage) { - using var tooltip = ImRaii.Tooltip(); + ImGui.BeginTooltip(); var scale = GetImageScaleFactor(texture); var textSize = ImGui.CalcTextSize(iconId.ToString()); - ImGui.SetCursorPosX((texture.Size.X * scale / 2.0f - (textSize.X / 2.0f)) + (ImGui.GetStyle().FramePadding.X * 2.0f)); + ImGui.SetCursorPosX( + texture.Size.X * scale / 2.0f - textSize.X / 2.0f + ImGui.GetStyle().FramePadding.X * 2.0f); ImGui.Text(iconId.ToString()); ImGui.Image(texture.Handle, texture.Size * scale); + ImGui.EndTooltip(); } - else if (ImGui.IsItemHovered()) // else, just draw the iconId. + + // else, just draw the iconId. + else if (ImGui.IsItemHovered()) { ImGui.SetTooltip(iconId.ToString()); } @@ -187,7 +185,10 @@ public class IconBrowserWidget : IDataWindowWidget Task.FromResult(texture.CreateWrapSharingLowLevelResource())); } - drawList.AddRect(cursor, cursor + this.iconSize, white); + ImGui.GetWindowDrawList().AddRect( + cursor, + cursor + this.iconSize, + ImGui.GetColorU32(ImGuiColors.DalamudWhite)); } else if (exc is not null) { @@ -196,13 +197,19 @@ public class IconBrowserWidget : IDataWindowWidget { var iconText = FontAwesomeIcon.Ban.ToIconString(); var textSize = ImGui.CalcTextSize(iconText); - drawList.AddText(cursor + ((this.iconSize - textSize) / 2), red, iconText); + ImGui.GetWindowDrawList().AddText( + cursor + ((this.iconSize - textSize) / 2), + ImGui.GetColorU32(ImGuiColors.DalamudRed), + iconText); } if (ImGui.IsItemHovered()) ImGui.SetTooltip($"{iconId}\n{exc}"); - drawList.AddRect(cursor, cursor + this.iconSize, red); + ImGui.GetWindowDrawList().AddRect( + cursor, + cursor + this.iconSize, + ImGui.GetColorU32(ImGuiColors.DalamudRed)); } else { @@ -211,12 +218,18 @@ public class IconBrowserWidget : IDataWindowWidget ImGui.Dummy(this.iconSize); var textSize = ImGui.CalcTextSize(text); - drawList.AddText(cursor + ((this.iconSize - textSize) / 2), color, text); + ImGui.GetWindowDrawList().AddText( + cursor + ((this.iconSize - textSize) / 2), + color, + text); if (ImGui.IsItemHovered()) ImGui.SetTooltip(iconId.ToString()); - drawList.AddRect(cursor, cursor + this.iconSize, color); + ImGui.GetWindowDrawList().AddRect( + cursor, + cursor + this.iconSize, + color); } } @@ -256,7 +269,7 @@ public class IconBrowserWidget : IDataWindowWidget if (this.valueRange is not null) return; - this.valueRange = []; + this.valueRange = new(); foreach (var (id, _) in this.iconIdsTask!.Result) { if (this.startRange <= id && id < this.stopRange) diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs index 2581f902d..d4afce48d 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs @@ -19,11 +19,11 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// </summary> internal class ImGuiWidget : IDataWindowWidget { - private readonly HashSet<IActiveNotification> notifications = []; + private readonly HashSet<IActiveNotification> notifications = new(); private NotificationTemplate notificationTemplate; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["imgui"]; + public string[]? CommandShortcuts { get; init; } = { "imgui" }; /// <inheritdoc/> public string DisplayName { get; init; } = "ImGui"; @@ -54,7 +54,8 @@ internal class ImGuiWidget : IDataWindowWidget ImGui.Separator(); - ImGui.Text($"WindowSystem.TimeSinceLastAnyFocus: {WindowSystem.TimeSinceLastAnyFocus.TotalMilliseconds:0}ms"); + ImGui.Text( + $"WindowSystem.TimeSinceLastAnyFocus: {WindowSystem.TimeSinceLastAnyFocus.TotalMilliseconds:0}ms"); ImGui.Separator(); @@ -78,24 +79,45 @@ internal class ImGuiWidget : IDataWindowWidget switch (this.notificationTemplate.IconInt) { case 1 or 2: - ImGui.InputText("Icon Text##iconText"u8, ref this.notificationTemplate.IconText, 255); + ImGui.InputText( + "Icon Text##iconText"u8, + ref this.notificationTemplate.IconText, + 255); break; case 5 or 6: - ImGui.Combo("Asset##iconAssetCombo", ref this.notificationTemplate.IconAssetInt, NotificationTemplate.AssetSources); + ImGui.Combo( + "Asset##iconAssetCombo", + ref this.notificationTemplate.IconAssetInt, + NotificationTemplate.AssetSources); break; case 3 or 7: - ImGui.InputText("Game Path##iconText"u8, ref this.notificationTemplate.IconText, 255); + ImGui.InputText( + "Game Path##iconText"u8, + ref this.notificationTemplate.IconText, + 255); break; case 4 or 8: - ImGui.InputText("File Path##iconText"u8, ref this.notificationTemplate.IconText, 255); + ImGui.InputText( + "File Path##iconText"u8, + ref this.notificationTemplate.IconText, + 255); break; } - ImGui.Combo("Initial Duration", ref this.notificationTemplate.InitialDurationInt, NotificationTemplate.InitialDurationTitles); + ImGui.Combo( + "Initial Duration", + ref this.notificationTemplate.InitialDurationInt, + NotificationTemplate.InitialDurationTitles); - ImGui.Combo("Extension Duration", ref this.notificationTemplate.HoverExtendDurationInt, NotificationTemplate.HoverExtendDurationTitles); + ImGui.Combo( + "Extension Duration", + ref this.notificationTemplate.HoverExtendDurationInt, + NotificationTemplate.HoverExtendDurationTitles); - ImGui.Combo("Progress", ref this.notificationTemplate.ProgressMode, NotificationTemplate.ProgressModeTitles); + ImGui.Combo( + "Progress", + ref this.notificationTemplate.ProgressMode, + NotificationTemplate.ProgressModeTitles); ImGui.Checkbox("Respect UI Hidden"u8, ref this.notificationTemplate.RespectUiHidden); @@ -105,11 +127,14 @@ internal class ImGuiWidget : IDataWindowWidget ImGui.Checkbox("User Dismissable"u8, ref this.notificationTemplate.UserDismissable); - ImGui.Checkbox("Action Bar (always on if not user dismissable for the example)"u8, ref this.notificationTemplate.ActionBar); + ImGui.Checkbox( + "Action Bar (always on if not user dismissable for the example)"u8, + ref this.notificationTemplate.ActionBar); if (ImGui.Button("Add notification"u8)) { - var text = "Bla bla bla bla bla bla bla bla bla bla bla.\nBla bla bla bla bla bla bla bla bla bla bla bla bla bla."; + var text = + "Bla bla bla bla bla bla bla bla bla bla bla.\nBla bla bla bla bla bla bla bla bla bla bla bla bla bla."; NewRandom(out var title, out var type, out var progress); if (this.notificationTemplate.ManualTitle) @@ -309,7 +334,7 @@ internal class ImGuiWidget : IDataWindowWidget private struct NotificationTemplate { public static readonly string[] IconTitles = - [ + { "None (use Type)", "SeIconChar", "FontAwesomeIcon", @@ -319,7 +344,7 @@ internal class ImGuiWidget : IDataWindowWidget "TextureWrap from DalamudAssets(Async)", "TextureWrap from GamePath", "TextureWrap from FilePath", - ]; + }; public static readonly string[] AssetSources = Enum.GetValues<DalamudAsset>() @@ -328,46 +353,46 @@ internal class ImGuiWidget : IDataWindowWidget .ToArray(); public static readonly string[] ProgressModeTitles = - [ + { "Default", "Random", "Increasing", "Increasing & Auto Dismiss", "Indeterminate", - ]; + }; public static readonly string[] TypeTitles = - [ + { nameof(NotificationType.None), nameof(NotificationType.Success), nameof(NotificationType.Warning), nameof(NotificationType.Error), nameof(NotificationType.Info), - ]; + }; public static readonly string[] InitialDurationTitles = - [ + { "Infinite", "1 seconds", "3 seconds (default)", "10 seconds", - ]; + }; public static readonly string[] HoverExtendDurationTitles = - [ + { "Disable", "1 seconds", "3 seconds (default)", "10 seconds", - ]; + }; public static readonly TimeSpan[] Durations = - [ + { TimeSpan.Zero, TimeSpan.FromSeconds(1), NotificationConstants.DefaultDuration, TimeSpan.FromSeconds(10), - ]; + }; public bool ManualContent; public string Content; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/InventoryWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/InventoryWidget.cs index 818e3fabc..c0ec3d490 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/InventoryWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/InventoryWidget.cs @@ -10,9 +10,7 @@ using Dalamud.Interface.Textures.Internal; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; - using FFXIVClientStructs.FFXIV.Client.Game; - using Lumina.Excel.Sheets; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -22,11 +20,6 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// </summary> internal class InventoryWidget : IDataWindowWidget { - private const ImGuiTableFlags TableFlags = ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders | - ImGuiTableFlags.ScrollY | ImGuiTableFlags.NoSavedSettings; - - private const ImGuiTableFlags InnerTableFlags = ImGuiTableFlags.BordersInner | ImGuiTableFlags.NoSavedSettings; - private DataManager dataManager; private TextureManager textureManager; private GameInventoryType? selectedInventoryType = GameInventoryType.Inventory1; @@ -69,7 +62,7 @@ internal class InventoryWidget : IDataWindowWidget private unsafe void DrawInventoryTypeList() { - using var table = ImRaii.Table("InventoryTypeTable"u8, 2, TableFlags, new Vector2(300, -1)); + using var table = ImRaii.Table("InventoryTypeTable"u8, 2, ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders | ImGuiTableFlags.ScrollY | ImGuiTableFlags.NoSavedSettings, new Vector2(300, -1)); if (!table) return; ImGui.TableSetupColumn("Type"u8); @@ -112,7 +105,7 @@ internal class InventoryWidget : IDataWindowWidget } } - private void DrawInventoryType(GameInventoryType inventoryType) + private unsafe void DrawInventoryType(GameInventoryType inventoryType) { var items = GameInventoryItem.GetReadOnlySpanOfInventory(inventoryType); if (items.IsEmpty) @@ -121,9 +114,8 @@ internal class InventoryWidget : IDataWindowWidget return; } - using var itemTable = ImRaii.Table("InventoryItemTable"u8, 4, TableFlags); + using var itemTable = ImRaii.Table("InventoryItemTable"u8, 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders | ImGuiTableFlags.ScrollY | ImGuiTableFlags.NoSavedSettings); if (!itemTable) return; - ImGui.TableSetupColumn("Slot"u8, ImGuiTableColumnFlags.WidthFixed, 40); ImGui.TableSetupColumn("ItemId"u8, ImGuiTableColumnFlags.WidthFixed, 70); ImGui.TableSetupColumn("Quantity"u8, ImGuiTableColumnFlags.WidthFixed, 70); @@ -135,7 +127,7 @@ internal class InventoryWidget : IDataWindowWidget { var item = items[slotIndex]; - using var disabledItem = ImRaii.Disabled(item.ItemId == 0); + using var disableditem = ImRaii.Disabled(item.ItemId == 0); ImGui.TableNextRow(); ImGui.TableNextColumn(); // Slot @@ -160,11 +152,11 @@ internal class InventoryWidget : IDataWindowWidget if (ImGui.IsItemHovered()) { ImGui.SetMouseCursor(ImGuiMouseCursor.Hand); - - using var tooltip = ImRaii.Tooltip(); + ImGui.BeginTooltip(); ImGui.Text("Click to copy IconId"u8); ImGui.Text($"ID: {iconId} – Size: {texture.Width}x{texture.Height}"); ImGui.Image(texture.Handle, new(texture.Width, texture.Height)); + ImGui.EndTooltip(); } if (ImGui.IsItemClicked()) @@ -175,7 +167,7 @@ internal class InventoryWidget : IDataWindowWidget using var itemNameColor = ImRaii.PushColor(ImGuiCol.Text, this.GetItemRarityColor(item.ItemId)); using var node = ImRaii.TreeNode($"{itemName}###{inventoryType}_{slotIndex}", ImGuiTreeNodeFlags.SpanAvailWidth); - itemNameColor.Pop(); + itemNameColor.Dispose(); using (var contextMenu = ImRaii.ContextPopupItem($"{inventoryType}_{slotIndex}_ContextMenu")) { @@ -190,7 +182,7 @@ internal class InventoryWidget : IDataWindowWidget if (!node) continue; - using var itemInfoTable = ImRaii.Table($"{inventoryType}_{slotIndex}_Table", 2, InnerTableFlags); + using var itemInfoTable = ImRaii.Table($"{inventoryType}_{slotIndex}_Table", 2, ImGuiTableFlags.BordersInner | ImGuiTableFlags.NoSavedSettings); if (!itemInfoTable) continue; ImGui.TableSetupColumn("Name"u8, ImGuiTableColumnFlags.WidthFixed, 150); @@ -272,7 +264,7 @@ internal class InventoryWidget : IDataWindowWidget ImGui.Text("Stains"u8); ImGui.TableNextColumn(); - using var stainTable = ImRaii.Table($"{inventoryType}_{slotIndex}_StainTable", 2, InnerTableFlags); + using var stainTable = ImRaii.Table($"{inventoryType}_{slotIndex}_StainTable", 2, ImGuiTableFlags.BordersInner | ImGuiTableFlags.NoSavedSettings); if (!stainTable) continue; ImGui.TableSetupColumn("Stain Id"u8, ImGuiTableColumnFlags.WidthFixed, 80); @@ -293,7 +285,7 @@ internal class InventoryWidget : IDataWindowWidget ImGui.Text("Materia"u8); ImGui.TableNextColumn(); - using var materiaTable = ImRaii.Table($"{inventoryType}_{slotIndex}_MateriaTable", 2, InnerTableFlags); + using var materiaTable = ImRaii.Table($"{inventoryType}_{slotIndex}_MateriaTable", 2, ImGuiTableFlags.BordersInner | ImGuiTableFlags.NoSavedSettings); if (!materiaTable) continue; ImGui.TableSetupColumn("Materia Id"u8, ImGuiTableColumnFlags.WidthFixed, 80); @@ -319,12 +311,10 @@ internal class InventoryWidget : IDataWindowWidget private uint GetItemRarityColor(uint itemId, bool isEdgeColor = false) { - var normalized = ItemUtil.GetBaseId(itemId); - - if (normalized.Kind == ItemKind.EventItem) + if (ItemUtil.IsEventItem(itemId)) return isEdgeColor ? 0xFF000000 : 0xFFFFFFFF; - if (!this.dataManager.Excel.GetSheet<Item>().TryGetRow(normalized.ItemId, out var item)) + if (!this.dataManager.Excel.GetSheet<Item>().TryGetRow(ItemUtil.GetBaseId(itemId).ItemId, out var item)) return isEdgeColor ? 0xFF000000 : 0xFFFFFFFF; var rowId = ItemUtil.GetItemRarityColorType(item.RowId, isEdgeColor); @@ -335,12 +325,18 @@ internal class InventoryWidget : IDataWindowWidget private uint GetItemIconId(uint itemId) { - var normalized = ItemUtil.GetBaseId(itemId); - // EventItem - if (normalized.Kind == ItemKind.EventItem) + if (ItemUtil.IsEventItem(itemId)) return this.dataManager.Excel.GetSheet<EventItem>().TryGetRow(itemId, out var eventItem) ? eventItem.Icon : 0u; - return this.dataManager.Excel.GetSheet<Item>().TryGetRow(normalized.ItemId, out var item) ? item.Icon : 0u; + // HighQuality + if (ItemUtil.IsHighQuality(itemId)) + itemId -= 1_000_000; + + // Collectible + if (ItemUtil.IsCollectible(itemId)) + itemId -= 500_000; + + return this.dataManager.Excel.GetSheet<Item>().TryGetRow(itemId, out var item) ? item.Icon : 0u; } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/KeyStateWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/KeyStateWidget.cs index d3b021f7a..fa615ed47 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/KeyStateWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/KeyStateWidget.cs @@ -1,7 +1,6 @@ -using Dalamud.Bindings.ImGui; +using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Keys; using Dalamud.Interface.Colors; -using Dalamud.Interface.Utility.Raii; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -11,7 +10,7 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; internal class KeyStateWidget : IDataWindowWidget { /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["keystate"]; + public string[]? CommandShortcuts { get; init; } = { "keystate" }; /// <inheritdoc/> public string DisplayName { get; init; } = "KeyState"; @@ -30,7 +29,6 @@ internal class KeyStateWidget : IDataWindowWidget { var keyState = Service<KeyState>.Get(); - // TODO: Use table instead of columns ImGui.Columns(4); var i = 0; @@ -39,10 +37,11 @@ internal class KeyStateWidget : IDataWindowWidget var code = (int)vkCode; var value = keyState[code]; - using (ImRaii.PushColor(ImGuiCol.Text, value ? ImGuiColors.HealerGreen : ImGuiColors.DPSRed)) - { - ImGui.Text($"{vkCode} ({code})"); - } + ImGui.PushStyleColor(ImGuiCol.Text, value ? ImGuiColors.HealerGreen : ImGuiColors.DPSRed); + + ImGui.Text($"{vkCode} ({code})"); + + ImGui.PopStyleColor(); i++; if (i % 24 == 0) diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/LogMessageMonitorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/LogMessageMonitorWidget.cs deleted file mode 100644 index fde46f0c7..000000000 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/LogMessageMonitorWidget.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System.Buffers; -using System.Collections.Concurrent; -using System.Linq; -using System.Text.Json; -using System.Text.RegularExpressions; - -using Dalamud.Bindings.ImGui; -using Dalamud.Game.Chat; -using Dalamud.Game.Gui; -using Dalamud.Interface.Utility; -using Dalamud.Interface.Utility.Raii; - -using Lumina.Text.ReadOnly; - -using ImGuiTable = Dalamud.Interface.Utility.ImGuiTable; - -namespace Dalamud.Interface.Internal.Windows.Data.Widgets; - -/// <summary> -/// Widget to display the LogMessages. -/// </summary> -internal class LogMessageMonitorWidget : IDataWindowWidget -{ - private readonly ConcurrentQueue<LogMessageData> messages = new(); - - private bool trackMessages; - private int trackedMessages; - private Regex? filterRegex; - private string filterString = string.Empty; - - /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["logmessage"]; - - /// <inheritdoc/> - public string DisplayName { get; init; } = "LogMessage Monitor"; - - /// <inheritdoc/> - public bool Ready { get; set; } - - /// <inheritdoc/> - public void Load() - { - this.trackMessages = false; - this.trackedMessages = 20; - this.filterRegex = null; - this.filterString = string.Empty; - this.messages.Clear(); - this.Ready = true; - } - - /// <inheritdoc/> - public void Draw() - { - var network = Service<ChatGui>.Get(); - if (ImGui.Checkbox("Track LogMessages"u8, ref this.trackMessages)) - { - if (this.trackMessages) - { - network.LogMessage += this.OnLogMessage; - } - else - { - network.LogMessage -= this.OnLogMessage; - } - } - - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X / 2); - if (ImGui.DragInt("Stored Number of Messages"u8, ref this.trackedMessages, 0.1f, 1, 512)) - { - this.trackedMessages = Math.Clamp(this.trackedMessages, 1, 512); - } - - if (ImGui.Button("Clear Stored Messages"u8)) - { - this.messages.Clear(); - } - - this.DrawFilterInput(); - - ImGuiTable.DrawTable(string.Empty, this.messages.Where(m => this.filterRegex == null || this.filterRegex.IsMatch(m.Formatted.ExtractText())), this.DrawNetworkPacket, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp, "LogMessageId", "Source", "Target", "Parameters", "Formatted"); - } - - private void DrawNetworkPacket(LogMessageData data) - { - ImGui.TableNextColumn(); - ImGui.Text(data.LogMessageId.ToString()); - - ImGui.TableNextColumn(); - ImGuiHelpers.SeStringWrapped(data.Source); - - ImGui.TableNextColumn(); - ImGuiHelpers.SeStringWrapped(data.Target); - - ImGui.TableNextColumn(); - ImGui.Text(data.Parameters); - - ImGui.TableNextColumn(); - ImGuiHelpers.SeStringWrapped(data.Formatted); - } - - private void DrawFilterInput() - { - var invalidRegEx = this.filterString.Length > 0 && this.filterRegex == null; - using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, invalidRegEx); - using var color = ImRaii.PushColor(ImGuiCol.Border, 0xFF0000FF, invalidRegEx); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - if (!ImGui.InputTextWithHint("##Filter"u8, "Regex Filter..."u8, ref this.filterString, 1024)) - { - return; - } - - if (this.filterString.Length == 0) - { - this.filterRegex = null; - } - else - { - try - { - this.filterRegex = new Regex(this.filterString, RegexOptions.Compiled | RegexOptions.ExplicitCapture); - } - catch - { - this.filterRegex = null; - } - } - } - - private void OnLogMessage(ILogMessage message) - { - var buffer = new ArrayBufferWriter<byte>(); - var writer = new Utf8JsonWriter(buffer); - - writer.WriteStartArray(); - for (var i = 0; i < message.ParameterCount; i++) - { - if (message.TryGetStringParameter(i, out var str)) - writer.WriteStringValue(str.ExtractText()); - else if (message.TryGetIntParameter(i, out var num)) - writer.WriteNumberValue(num); - else - writer.WriteNullValue(); - } - - writer.WriteEndArray(); - writer.Flush(); - - this.messages.Enqueue(new LogMessageData(message.LogMessageId, message.SourceEntity?.Name ?? default, message.TargetEntity?.Name ?? default, buffer.WrittenMemory, message.FormatLogMessageForDebugging())); - while (this.messages.Count > this.trackedMessages) - { - this.messages.TryDequeue(out _); - } - } - - private readonly record struct LogMessageData(uint LogMessageId, ReadOnlySeString Source, ReadOnlySeString Target, ReadOnlyMemory<byte> Parameters, ReadOnlySeString Formatted); -} diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/MarketBoardWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/MarketBoardWidget.cs index a74c5f3d3..56de19de7 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/MarketBoardWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/MarketBoardWidget.cs @@ -1,9 +1,10 @@ -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Globalization; using Dalamud.Bindings.ImGui; using Dalamud.Game.MarketBoard; using Dalamud.Game.Network.Structures; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using ImGuiTable = Dalamud.Interface.Utility.ImGuiTable; @@ -15,8 +16,6 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// </summary> internal class MarketBoardWidget : IDataWindowWidget { - private const ImGuiTableFlags TableFlags = ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg; - private readonly ConcurrentQueue<(IMarketBoardHistory MarketBoardHistory, IMarketBoardHistoryListing Listing)> marketBoardHistoryQueue = new(); private readonly ConcurrentQueue<(IMarketBoardCurrentOfferings MarketBoardCurrentOfferings, IMarketBoardItemListing Listing)> marketBoardCurrentOfferingsQueue = new(); private readonly ConcurrentQueue<IMarketBoardPurchase> marketBoardPurchasesQueue = new(); @@ -45,7 +44,7 @@ internal class MarketBoardWidget : IDataWindowWidget } /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["marketboard"]; + public string[]? CommandShortcuts { get; init; } = { "marketboard" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Market Board"; @@ -101,47 +100,49 @@ internal class MarketBoardWidget : IDataWindowWidget this.marketBoardHistoryQueue.Clear(); } - using var tabBar = ImRaii.TabBar("marketTabs"u8); - if (!tabBar.Success) - return; - - using (var tabItem = ImRaii.TabItem("History"u8)) + using (var tabBar = ImRaii.TabBar("marketTabs"u8)) { - if (tabItem) + if (tabBar) { - ImGuiTable.DrawTable("history-table", this.marketBoardHistoryQueue, this.DrawMarketBoardHistory, TableFlags, "Item ID", "Quantity", "Is HQ?", "Sale Price", "Buyer Name", "Purchase Time"); - } - } + using (var tabItem = ImRaii.TabItem("History"u8)) + { + if (tabItem) + { + ImGuiTable.DrawTable(string.Empty, this.marketBoardHistoryQueue, this.DrawMarketBoardHistory, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Item ID", "Quantity", "Is HQ?", "Sale Price", "Buyer Name", "Purchase Time"); + } + } - using (var tabItem = ImRaii.TabItem("Offerings"u8)) - { - if (tabItem) - { - ImGuiTable.DrawTable("offerings-table", this.marketBoardCurrentOfferingsQueue, this.DrawMarketBoardCurrentOfferings, TableFlags, "Item ID", "Quantity", "Is HQ?", "Price Per Unit", "Retainer Name"); - } - } + using (var tabItem = ImRaii.TabItem("Offerings"u8)) + { + if (tabItem) + { + ImGuiTable.DrawTable(string.Empty, this.marketBoardCurrentOfferingsQueue, this.DrawMarketBoardCurrentOfferings, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Item ID", "Quantity", "Is HQ?", "Price Per Unit", "Retainer Name"); + } + } - using (var tabItem = ImRaii.TabItem("Purchases"u8)) - { - if (tabItem) - { - ImGuiTable.DrawTable("purchases-table", this.marketBoardPurchasesQueue, this.DrawMarketBoardPurchases, TableFlags, "Item ID", "Quantity"); - } - } + using (var tabItem = ImRaii.TabItem("Purchases"u8)) + { + if (tabItem) + { + ImGuiTable.DrawTable(string.Empty, this.marketBoardPurchasesQueue, this.DrawMarketBoardPurchases, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Item ID", "Quantity"); + } + } - using (var tabItem = ImRaii.TabItem("Purchase Requests"u8)) - { - if (tabItem) - { - ImGuiTable.DrawTable("requests-table", this.marketBoardPurchaseRequestsQueue, this.DrawMarketBoardPurchaseRequests, TableFlags, "Item ID", "Is HQ?", "Quantity", "Price Per Unit", "Total Tax", "City ID", "Listing ID", "Retainer ID"); - } - } + using (var tabItem = ImRaii.TabItem("Purchase Requests"u8)) + { + if (tabItem) + { + ImGuiTable.DrawTable(string.Empty, this.marketBoardPurchaseRequestsQueue, this.DrawMarketBoardPurchaseRequests, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Item ID", "Is HQ?", "Quantity", "Price Per Unit", "Total Tax", "City ID", "Listing ID", "Retainer ID"); + } + } - using (var tabItem = ImRaii.TabItem("Taxes"u8)) - { - if (tabItem) - { - ImGuiTable.DrawTable("taxes-table", this.marketTaxRatesQueue, this.DrawMarketTaxRates, TableFlags, "Uldah", "Limsa Lominsa", "Gridania", "Ishgard", "Kugane", "Crystarium", "Sharlayan", "Tuliyollal", "Valid Until"); + using (var tabItem = ImRaii.TabItem("Taxes"u8)) + { + if (tabItem) + { + ImGuiTable.DrawTable(string.Empty, this.marketTaxRatesQueue, this.DrawMarketTaxRates, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Uldah", "Limsa Lominsa", "Gridania", "Ishgard", "Kugane", "Crystarium", "Sharlayan", "Tuliyollal", "Valid Until"); + } + } } } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/NetworkMonitorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/NetworkMonitorWidget.cs index 73916761b..4a32a16df 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/NetworkMonitorWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/NetworkMonitorWidget.cs @@ -1,53 +1,48 @@ using System.Collections.Concurrent; -using System.Threading; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; using Dalamud.Bindings.ImGui; -using Dalamud.Hooking; -using Dalamud.Interface.Components; +using Dalamud.Game.Network; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; +using Dalamud.Memory; -using FFXIVClientStructs.FFXIV.Application.Network; -using FFXIVClientStructs.FFXIV.Client.Game; -using FFXIVClientStructs.FFXIV.Client.Game.Object; -using FFXIVClientStructs.FFXIV.Client.Game.UI; -using FFXIVClientStructs.FFXIV.Client.Network; +using ImGuiTable = Dalamud.Interface.Utility.ImGuiTable; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// <summary> /// Widget to display the current packets. /// </summary> -internal unsafe class NetworkMonitorWidget : IDataWindowWidget +internal class NetworkMonitorWidget : IDataWindowWidget { private readonly ConcurrentQueue<NetworkPacketData> packets = new(); - private Hook<PacketDispatcher.Delegates.OnReceivePacket>? hookZoneDown; - private Hook<ZoneClient.Delegates.SendPacket>? hookZoneUp; - - private bool trackZoneUp; - private bool trackZoneDown; - private int trackedPackets = 20; - private ulong nextPacketIndex; + private bool trackNetwork; + private int trackedPackets; + private Regex? trackedOpCodes; private string filterString = string.Empty; - private bool filterRecording = true; - private bool autoScroll = true; - private bool autoScrollPending; + private Regex? untrackedOpCodes; + private string negativeFilterString = string.Empty; /// <summary> Finalizes an instance of the <see cref="NetworkMonitorWidget"/> class. </summary> ~NetworkMonitorWidget() { - this.hookZoneDown?.Dispose(); - this.hookZoneUp?.Dispose(); - } - - private enum NetworkMessageDirection - { - ZoneDown, - ZoneUp, + if (this.trackNetwork) + { + this.trackNetwork = false; + var network = Service<GameNetwork>.GetNullable(); + if (network != null) + { + network.NetworkMessage -= this.OnNetworkMessage; + } + } } /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["network", "netmon", "networkmonitor"]; + public string[]? CommandShortcuts { get; init; } = { "network", "netmon", "networkmonitor" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Network Monitor"; @@ -56,50 +51,33 @@ internal unsafe class NetworkMonitorWidget : IDataWindowWidget public bool Ready { get; set; } /// <inheritdoc/> - public void Load() => this.Ready = true; + public void Load() + { + this.trackNetwork = false; + this.trackedPackets = 20; + this.trackedOpCodes = null; + this.filterString = string.Empty; + this.packets.Clear(); + this.Ready = true; + } /// <inheritdoc/> public void Draw() { - this.hookZoneDown ??= Hook<PacketDispatcher.Delegates.OnReceivePacket>.FromAddress( - (nint)PacketDispatcher.StaticVirtualTablePointer->OnReceivePacket, - this.OnReceivePacketDetour); - - this.hookZoneUp ??= Hook<ZoneClient.Delegates.SendPacket>.FromAddress( - (nint)ZoneClient.MemberFunctionPointers.SendPacket, - this.SendPacketDetour); - - if (ImGui.Checkbox("Track ZoneUp"u8, ref this.trackZoneUp)) + var network = Service<GameNetwork>.Get(); + if (ImGui.Checkbox("Track Network Packets"u8, ref this.trackNetwork)) { - if (this.trackZoneUp) + if (this.trackNetwork) { - if (!this.trackZoneDown) - this.nextPacketIndex = 0; - - this.hookZoneUp?.Enable(); + network.NetworkMessage += this.OnNetworkMessage; } else { - this.hookZoneUp?.Disable(); + network.NetworkMessage -= this.OnNetworkMessage; } } - if (ImGui.Checkbox("Track ZoneDown"u8, ref this.trackZoneDown)) - { - if (this.trackZoneDown) - { - if (!this.trackZoneUp) - this.nextPacketIndex = 0; - - this.hookZoneDown?.Enable(); - } - else - { - this.hookZoneDown?.Disable(); - } - } - - ImGui.SetNextItemWidth(-1); + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X / 2); if (ImGui.DragInt("Stored Number of Packets"u8, ref this.trackedPackets, 0.1f, 1, 512)) { this.trackedPackets = Math.Clamp(this.trackedPackets, 1, 512); @@ -108,203 +86,133 @@ internal unsafe class NetworkMonitorWidget : IDataWindowWidget if (ImGui.Button("Clear Stored Packets"u8)) { this.packets.Clear(); - this.nextPacketIndex = 0; } - ImGui.SameLine(); - ImGui.Checkbox("Auto-Scroll"u8, ref this.autoScroll); + this.DrawFilterInput(); + this.DrawNegativeFilterInput(); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - (ImGui.GetStyle().ItemInnerSpacing.X + ImGui.GetFrameHeight()) * 2); - ImGui.InputTextWithHint("##Filter"u8, "Filter OpCodes..."u8, ref this.filterString, 1024, ImGuiInputTextFlags.AutoSelectAll); - ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X); - ImGui.Checkbox("##FilterRecording"u8, ref this.filterRecording); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip("When enabled, packets are filtered before being recorded.\nWhen disabled, all packets are recorded and filtering only affects packets displayed in the table."u8); - ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X); - ImGuiComponents.HelpMarker("Enter OpCodes in a comma-separated list.\nRanges are supported. Exclude OpCodes with exclamation mark.\nExample: -400,!50-100,650,700-980,!941"); + ImGuiTable.DrawTable(string.Empty, this.packets, this.DrawNetworkPacket, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Direction", "OpCode", "Hex", "Target", "Source", "Data"); + } - using var table = ImRaii.Table("NetworkMonitorTableV2"u8, 6, ImGuiTableFlags.Borders | ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | ImGuiTableFlags.NoSavedSettings); - if (!table) return; + private void DrawNetworkPacket(NetworkPacketData data) + { + ImGui.TableNextColumn(); + ImGui.Text(data.Direction.ToString()); - ImGui.TableSetupColumn("Index"u8, ImGuiTableColumnFlags.WidthFixed, 50); - ImGui.TableSetupColumn("Time"u8, ImGuiTableColumnFlags.WidthFixed, 100); - ImGui.TableSetupColumn("Direction"u8, ImGuiTableColumnFlags.WidthFixed, 100); - ImGui.TableSetupColumn("OpCode"u8, ImGuiTableColumnFlags.WidthFixed, 100); - ImGui.TableSetupColumn("OpCode (Hex)"u8, ImGuiTableColumnFlags.WidthFixed, 100); - ImGui.TableSetupColumn("Target EntityId"u8, ImGuiTableColumnFlags.WidthStretch); - ImGui.TableSetupScrollFreeze(0, 1); - ImGui.TableHeadersRow(); + ImGui.TableNextColumn(); + ImGui.Text(data.OpCode.ToString()); - var autoScrollDisabled = false; + ImGui.TableNextColumn(); + ImGui.Text($"0x{data.OpCode:X4}"); - foreach (var packet in this.packets) + ImGui.TableNextColumn(); + ImGui.Text(data.TargetActorId > 0 ? $"0x{data.TargetActorId:X}" : string.Empty); + + ImGui.TableNextColumn(); + ImGui.Text(data.SourceActorId > 0 ? $"0x{data.SourceActorId:X}" : string.Empty); + + ImGui.TableNextColumn(); + if (data.Data.Count > 0) { - if (!this.filterRecording && !this.IsFiltered(packet.OpCode)) - continue; - - ImGui.TableNextColumn(); - ImGui.Text(packet.Index.ToString()); - - ImGui.TableNextColumn(); - ImGui.Text(packet.Time.ToLongTimeString()); - - ImGui.TableNextColumn(); - ImGui.Text(packet.Direction.ToString()); - - ImGui.TableNextColumn(); - using (ImRaii.PushId(packet.Index.ToString())) - { - if (ImGui.SmallButton("X")) - { - if (!string.IsNullOrEmpty(this.filterString)) - this.filterString += ","; - - this.filterString += $"!{packet.OpCode}"; - } - } - - if (ImGui.IsItemHovered()) - ImGui.SetTooltip("Filter OpCode"u8); - - autoScrollDisabled |= ImGui.IsItemHovered(); - - ImGui.SameLine(); - WidgetUtil.DrawCopyableText(packet.OpCode.ToString()); - autoScrollDisabled |= ImGui.IsItemHovered(); - - ImGui.TableNextColumn(); - WidgetUtil.DrawCopyableText($"0x{packet.OpCode:X3}"); - autoScrollDisabled |= ImGui.IsItemHovered(); - - ImGui.TableNextColumn(); - if (packet.TargetEntityId > 0) - { - WidgetUtil.DrawCopyableText($"{packet.TargetEntityId:X}"); - - var name = !string.IsNullOrEmpty(packet.TargetName) - ? packet.TargetName - : GetTargetName(packet.TargetEntityId); - - if (!string.IsNullOrEmpty(name)) - { - ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X); - ImGui.Text($"({name})"); - } - } + ImGui.Text(string.Join(" ", data.Data.Select(b => b.ToString("X2")))); } - - if (this.autoScroll && this.autoScrollPending && !autoScrollDisabled) + else { - ImGui.SetScrollHereY(); - this.autoScrollPending = false; + ImGui.Dummy(ImGui.GetContentRegionAvail() with { Y = 0 }); } } - private static string GetTargetName(uint targetId) + private void DrawFilterInput() { - if (targetId == PlayerState.Instance()->EntityId) - return "Local Player"; - - var cachedName = NameCache.Instance()->GetNameByEntityId(targetId); - if (cachedName.HasValue) - return cachedName.ToString(); - - var obj = GameObjectManager.Instance()->Objects.GetObjectByEntityId(targetId); - if (obj != null) - return obj->NameString; - - return string.Empty; - } - - private void OnReceivePacketDetour(PacketDispatcher* thisPtr, uint targetId, nint packet) - { - var opCode = *(ushort*)(packet + 2); - var targetName = GetTargetName(targetId); - this.RecordPacket(new NetworkPacketData(Interlocked.Increment(ref this.nextPacketIndex), DateTime.Now, opCode, NetworkMessageDirection.ZoneDown, targetId, targetName)); - this.hookZoneDown.OriginalDisposeSafe(thisPtr, targetId, packet); - } - - private bool SendPacketDetour(ZoneClient* thisPtr, nint packet, uint a3, uint a4, bool a5) - { - var opCode = *(ushort*)packet; - this.RecordPacket(new NetworkPacketData(Interlocked.Increment(ref this.nextPacketIndex), DateTime.Now, opCode, NetworkMessageDirection.ZoneUp, 0, string.Empty)); - return this.hookZoneUp.OriginalDisposeSafe(thisPtr, packet, a3, a4, a5); - } - - private void RecordPacket(NetworkPacketData packet) - { - if (this.filterRecording && !this.IsFiltered(packet.OpCode)) + var invalidRegEx = this.filterString.Length > 0 && this.trackedOpCodes == null; + using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, invalidRegEx); + using var color = ImRaii.PushColor(ImGuiCol.Border, 0xFF0000FF, invalidRegEx); + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); + if (!ImGui.InputTextWithHint("##Filter"u8, "Regex Filter OpCodes..."u8, ref this.filterString, 1024)) + { return; - - this.packets.Enqueue(packet); - - while (this.packets.Count > this.trackedPackets) - { - this.packets.TryDequeue(out _); } - this.autoScrollPending = true; - } - - private bool IsFiltered(ushort opcode) - { - var filterString = this.filterString.Replace(" ", string.Empty); - - if (filterString.Length == 0) - return true; - - try + if (this.filterString.Length == 0) { - var offset = 0; - var included = false; - var hasInclude = false; - - while (filterString.Length - offset > 0) + this.trackedOpCodes = null; + } + else + { + try { - var remaining = filterString[offset..]; - - // find the end of the current entry - var entryEnd = remaining.IndexOf(','); - if (entryEnd == -1) - entryEnd = remaining.Length; - - var entry = filterString[offset..(offset + entryEnd)]; - var dash = entry.IndexOf('-'); - var isExcluded = entry.StartsWith('!'); - var startOffset = isExcluded ? 1 : 0; - - var entryMatch = dash == -1 - ? ushort.Parse(entry[startOffset..]) == opcode - : ((dash - startOffset == 0 || opcode >= ushort.Parse(entry[startOffset..dash])) - && (entry[(dash + 1)..].Length == 0 || opcode <= ushort.Parse(entry[(dash + 1)..]))); - - if (isExcluded) - { - if (entryMatch) - return false; - } - else - { - hasInclude = true; - included |= entryMatch; - } - - if (entryEnd == filterString.Length) - break; - - offset += entryEnd + 1; + this.trackedOpCodes = new Regex(this.filterString, RegexOptions.Compiled | RegexOptions.ExplicitCapture); + } + catch + { + this.trackedOpCodes = null; } - - return !hasInclude || included; - } - catch (Exception ex) - { - Serilog.Log.Error(ex, "Invalid filter string"); - return false; } } + private void DrawNegativeFilterInput() + { + var invalidRegEx = this.negativeFilterString.Length > 0 && this.untrackedOpCodes == null; + using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, invalidRegEx); + using var color = ImRaii.PushColor(ImGuiCol.Border, 0xFF0000FF, invalidRegEx); + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); + if (!ImGui.InputTextWithHint("##NegativeFilter"u8, "Regex Filter Against OpCodes..."u8, ref this.negativeFilterString, 1024)) + { + return; + } + + if (this.negativeFilterString.Length == 0) + { + this.untrackedOpCodes = null; + } + else + { + try + { + this.untrackedOpCodes = new Regex(this.negativeFilterString, RegexOptions.Compiled | RegexOptions.ExplicitCapture); + } + catch + { + this.untrackedOpCodes = null; + } + } + } + + private void OnNetworkMessage(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction) + { + if ((this.trackedOpCodes == null || this.trackedOpCodes.IsMatch(this.OpCodeToString(opCode))) + && (this.untrackedOpCodes == null || !this.untrackedOpCodes.IsMatch(this.OpCodeToString(opCode)))) + { + this.packets.Enqueue(new NetworkPacketData(this, opCode, direction, sourceActorId, targetActorId, dataPtr)); + while (this.packets.Count > this.trackedPackets) + { + this.packets.TryDequeue(out _); + } + } + } + + private int GetSizeFromOpCode(ushort opCode) + => 0; + + /// <remarks> Add known packet-name -> packet struct size associations here to copy the byte data for such packets. </remarks>> + private int GetSizeFromName(string name) + => name switch + { + _ => 0, + }; + + /// <remarks> The filter should find opCodes by number (decimal and hex) and name, if existing. </remarks> + private string OpCodeToString(ushort opCode) + => $"{opCode}\0{opCode:X}"; + #pragma warning disable SA1313 - private readonly record struct NetworkPacketData(ulong Index, DateTime Time, ushort OpCode, NetworkMessageDirection Direction, uint TargetEntityId, string TargetName); + private readonly record struct NetworkPacketData(ushort OpCode, NetworkMessageDirection Direction, uint SourceActorId, uint TargetActorId) #pragma warning restore SA1313 + { + public readonly IReadOnlyList<byte> Data = Array.Empty<byte>(); + + public NetworkPacketData(NetworkMonitorWidget widget, ushort opCode, NetworkMessageDirection direction, uint sourceActorId, uint targetActorId, nint dataPtr) + : this(opCode, direction, sourceActorId, targetActorId) + => this.Data = MemoryHelper.Read<byte>(dataPtr, widget.GetSizeFromOpCode(opCode), false); + } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/NounProcessorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/NounProcessorWidget.cs index 968254ceb..cbf5c3355 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/NounProcessorWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/NounProcessorWidget.cs @@ -8,7 +8,6 @@ using Dalamud.Game.ClientState; using Dalamud.Game.Text.Noun; using Dalamud.Game.Text.Noun.Enums; using Dalamud.Interface.Utility.Raii; - using Lumina.Data; using Lumina.Excel; using Lumina.Excel.Sheets; @@ -55,13 +54,13 @@ internal class NounProcessorWidget : IDataWindowWidget private ClientLanguage[] languages = []; private string[] languageNames = []; - private int selectedSheetNameIndex; - private int selectedLanguageIndex; + private int selectedSheetNameIndex = 0; + private int selectedLanguageIndex = 0; private int rowId = 1; private int amount = 1; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["noun"]; + public string[]? CommandShortcuts { get; init; } = { "noun" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Noun Processor"; @@ -84,6 +83,7 @@ internal class NounProcessorWidget : IDataWindowWidget { var nounProcessor = Service<NounProcessor>.Get(); var dataManager = Service<DataManager>.Get(); + var clientState = Service<ClientState>.Get(); var sheetType = NounSheets.ElementAt(this.selectedSheetNameIndex); var language = this.languages[this.selectedLanguageIndex]; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs index 6a6c0f8be..71fb18352 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs @@ -14,17 +14,12 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// </summary> internal class ObjectTableWidget : IDataWindowWidget { - private const ImGuiWindowFlags CharacterWindowFlags = ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.AlwaysAutoResize | - ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoMove | - ImGuiWindowFlags.NoMouseInputs | ImGuiWindowFlags.NoDocking | - ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNav; - private bool resolveGameData; private bool drawCharacters; private float maxCharaDrawDistance = 20.0f; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["ot", "objecttable"]; + public string[]? CommandShortcuts { get; init; } = { "ot", "objecttable" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Object Table"; @@ -54,66 +49,74 @@ internal class ObjectTableWidget : IDataWindowWidget if (objectTable.LocalPlayer == null) { ImGui.Text("LocalPlayer null."u8); - return; } - - if (clientState.IsPvPExcludingDen) + else if (clientState.IsPvPExcludingDen) { ImGui.Text("Cannot access object table while in PvP."u8); - return; } - - stateString += $"ObjectTableLen: {objectTable.Length}\n"; - stateString += $"LocalPlayerName: {playerState.CharacterName}\n"; - stateString += $"CurrentWorldName: {(this.resolveGameData ? playerState.CurrentWorld.ValueNullable?.Name : playerState.CurrentWorld.RowId.ToString())}\n"; - stateString += $"HomeWorldName: {(this.resolveGameData ? playerState.HomeWorld.ValueNullable?.Name : playerState.HomeWorld.RowId.ToString())}\n"; - stateString += $"LocalCID: {playerState.ContentId:X}\n"; - stateString += $"LastLinkedItem: {chatGui.LastLinkedItemId}\n"; - stateString += $"TerritoryType: {clientState.TerritoryType}\n\n"; - - ImGui.Text(stateString); - - ImGui.Checkbox("Draw characters on screen"u8, ref this.drawCharacters); - ImGui.SliderFloat("Draw Distance"u8, ref this.maxCharaDrawDistance, 2f, 40f); - - for (var i = 0; i < objectTable.Length; i++) + else { - var obj = objectTable[i]; + stateString += $"ObjectTableLen: {objectTable.Length}\n"; + stateString += $"LocalPlayerName: {playerState.CharacterName}\n"; + stateString += $"CurrentWorldName: {(this.resolveGameData ? playerState.CurrentWorld.ValueNullable?.Name : playerState.CurrentWorld.RowId.ToString())}\n"; + stateString += $"HomeWorldName: {(this.resolveGameData ? playerState.HomeWorld.ValueNullable?.Name : playerState.HomeWorld.RowId.ToString())}\n"; + stateString += $"LocalCID: {playerState.ContentId:X}\n"; + stateString += $"LastLinkedItem: {chatGui.LastLinkedItemId}\n"; + stateString += $"TerritoryType: {clientState.TerritoryType}\n\n"; - if (obj == null) - continue; + ImGui.Text(stateString); - Util.PrintGameObject(obj, i.ToString(), this.resolveGameData); + ImGui.Checkbox("Draw characters on screen"u8, ref this.drawCharacters); + ImGui.SliderFloat("Draw Distance"u8, ref this.maxCharaDrawDistance, 2f, 40f); - if (this.drawCharacters && gameGui.WorldToScreen(obj.Position, out var screenCoords)) + for (var i = 0; i < objectTable.Length; i++) { - // So, while WorldToScreen will return false if the point is off of game client screen, to - // to avoid performance issues, we have to manually determine if creating a window would - // produce a new viewport, and skip rendering it if so - var objectText = $"{obj.Address:X}:{obj.GameObjectId:X}[{i}] - {obj.ObjectKind} - {obj.Name}"; + var obj = objectTable[i]; - var screenPos = ImGui.GetMainViewport().Pos; - var screenSize = ImGui.GetMainViewport().Size; - - var windowSize = ImGui.CalcTextSize(objectText); - - // Add some extra safety padding - windowSize.X += ImGui.GetStyle().WindowPadding.X + 10; - windowSize.Y += ImGui.GetStyle().WindowPadding.Y + 10; - - if (screenCoords.X + windowSize.X > screenPos.X + screenSize.X || - screenCoords.Y + windowSize.Y > screenPos.Y + screenSize.Y) + if (obj == null) continue; - if (obj.YalmDistanceX > this.maxCharaDrawDistance) - continue; + Util.PrintGameObject(obj, i.ToString(), this.resolveGameData); - ImGui.SetNextWindowPos(new Vector2(screenCoords.X, screenCoords.Y)); - ImGui.SetNextWindowBgAlpha(Math.Max(1f - (obj.YalmDistanceX / this.maxCharaDrawDistance), 0.2f)); + if (this.drawCharacters && gameGui.WorldToScreen(obj.Position, out var screenCoords)) + { + // So, while WorldToScreen will return false if the point is off of game client screen, to + // to avoid performance issues, we have to manually determine if creating a window would + // produce a new viewport, and skip rendering it if so + var objectText = $"{obj.Address.ToInt64():X}:{obj.GameObjectId:X}[{i}] - {obj.ObjectKind} - {obj.Name}"; - if (ImGui.Begin($"Actor{i}##ActorWindow{i}", CharacterWindowFlags)) - ImGui.Text(objectText); - ImGui.End(); + var screenPos = ImGui.GetMainViewport().Pos; + var screenSize = ImGui.GetMainViewport().Size; + + var windowSize = ImGui.CalcTextSize(objectText); + + // Add some extra safety padding + windowSize.X += ImGui.GetStyle().WindowPadding.X + 10; + windowSize.Y += ImGui.GetStyle().WindowPadding.Y + 10; + + if (screenCoords.X + windowSize.X > screenPos.X + screenSize.X || + screenCoords.Y + windowSize.Y > screenPos.Y + screenSize.Y) + continue; + + if (obj.YalmDistanceX > this.maxCharaDrawDistance) + continue; + + ImGui.SetNextWindowPos(new Vector2(screenCoords.X, screenCoords.Y)); + + ImGui.SetNextWindowBgAlpha(Math.Max(1f - (obj.YalmDistanceX / this.maxCharaDrawDistance), 0.2f)); + if (ImGui.Begin( + $"Actor{i}##ActorWindow{i}", + ImGuiWindowFlags.NoDecoration | + ImGuiWindowFlags.AlwaysAutoResize | + ImGuiWindowFlags.NoSavedSettings | + ImGuiWindowFlags.NoMove | + ImGuiWindowFlags.NoMouseInputs | + ImGuiWindowFlags.NoDocking | + ImGuiWindowFlags.NoFocusOnAppearing | + ImGuiWindowFlags.NoNav)) + ImGui.Text(objectText); + ImGui.End(); + } } } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/PartyListWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/PartyListWidget.cs index 597078f21..e43b231be 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/PartyListWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/PartyListWidget.cs @@ -1,4 +1,4 @@ -using Dalamud.Bindings.ImGui; +using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Party; using Dalamud.Utility; @@ -12,7 +12,7 @@ internal class PartyListWidget : IDataWindowWidget private bool resolveGameData; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["partylist", "party"]; + public string[]? CommandShortcuts { get; init; } = { "partylist", "party" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Party List"; @@ -33,9 +33,9 @@ internal class PartyListWidget : IDataWindowWidget ImGui.Checkbox("Resolve GameData"u8, ref this.resolveGameData); - ImGui.Text($"GroupManager: {partyList.GroupManagerAddress:X}"); - ImGui.Text($"GroupList: {partyList.GroupListAddress:X}"); - ImGui.Text($"AllianceList: {partyList.AllianceListAddress:X}"); + ImGui.Text($"GroupManager: {partyList.GroupManagerAddress.ToInt64():X}"); + ImGui.Text($"GroupList: {partyList.GroupListAddress.ToInt64():X}"); + ImGui.Text($"AllianceList: {partyList.AllianceListAddress.ToInt64():X}"); ImGui.Text($"{partyList.Length} Members"); @@ -48,7 +48,7 @@ internal class PartyListWidget : IDataWindowWidget continue; } - ImGui.Text($"[{i}] {member.Address:X} - {member.Name} - {member.GameObject?.GameObjectId ?? 0}"); + ImGui.Text($"[{i}] {member.Address.ToInt64():X} - {member.Name} - {member.GameObject?.GameObjectId}"); if (this.resolveGameData) { var actor = member.GameObject; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs index 72a02b219..0ca754a91 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs @@ -25,7 +25,7 @@ internal class PluginIpcWidget : IDataWindowWidget private string callGateResponse = string.Empty; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["ipc"]; + public string[]? CommandShortcuts { get; init; } = { "ipc" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Plugin IPC"; @@ -48,20 +48,12 @@ internal class PluginIpcWidget : IDataWindowWidget this.ipcPub.RegisterAction(msg => { - Log.Information( - "Data action was called: {Msg}\n" + - " Context: {Context}", - msg, - this.ipcPub.GetContext()); + Log.Information("Data action was called: {Msg}", msg); }); this.ipcPub.RegisterFunc(msg => { - Log.Information( - "Data func was called: {Msg}\n" + - " Context: {Context}", - msg, - this.ipcPub.GetContext()); + Log.Information("Data func was called: {Msg}", msg); return Guid.NewGuid().ToString(); }); } @@ -69,8 +61,14 @@ internal class PluginIpcWidget : IDataWindowWidget if (this.ipcSub == null) { this.ipcSub = new CallGatePubSub<string, string>("dataDemo1"); - this.ipcSub.Subscribe(_ => { Log.Information("PONG1"); }); - this.ipcSub.Subscribe(_ => { Log.Information("PONG2"); }); + this.ipcSub.Subscribe(_ => + { + Log.Information("PONG1"); + }); + this.ipcSub.Subscribe(_ => + { + Log.Information("PONG2"); + }); this.ipcSub.Subscribe(_ => throw new Exception("PONG3")); } @@ -80,21 +78,12 @@ internal class PluginIpcWidget : IDataWindowWidget this.ipcPubGo.RegisterAction(go => { - Log.Information( - "Data action was called: {Name}" + - "\n Context: {Context}", - go?.Name, - this.ipcPubGo.GetContext()); + Log.Information("Data action was called: {Name}", go?.Name); }); this.ipcPubGo.RegisterFunc(go => { - Log.Information( - "Data func was called: {Name}\n" + - " Context: {Context}", - go?.Name, - this.ipcPubGo.GetContext()); - + Log.Information("Data func was called: {Name}", go?.Name); return "test"; }); } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeFontTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeFontTestWidget.cs index 87c0afe58..17b7959f6 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeFontTestWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeFontTestWidget.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using Dalamud.Bindings.ImGui; using Dalamud.Game.Text; @@ -11,7 +11,7 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; internal class SeFontTestWidget : IDataWindowWidget { /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["sefont", "sefonttest"]; + public string[]? CommandShortcuts { get; init; } = { "sefont", "sefonttest" }; /// <inheritdoc/> public string DisplayName { get; init; } = "SeFont Test"; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs index a11bc3719..a88f576f9 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs @@ -29,6 +29,8 @@ using Lumina.Text.Expressions; using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; +using LSeStringBuilder = Lumina.Text.SeStringBuilder; + namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// <summary> @@ -38,11 +40,6 @@ internal class SeStringCreatorWidget : IDataWindowWidget { private const LinkMacroPayloadType DalamudLinkType = (LinkMacroPayloadType)Payload.EmbeddedInfoType.DalamudLink - 1; - private const ImGuiTableFlags TableFlags = ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | - ImGuiTableFlags.ScrollY | ImGuiTableFlags.NoSavedSettings; - - private static readonly string[] TextEntryTypeOptions = ["String", "Macro", "Fixed"]; - private readonly Dictionary<MacroCode, string[]> expressionNames = new() { { MacroCode.SetResetTime, ["Hour", "WeekDay"] }, @@ -147,7 +144,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget new TextEntry(TextEntryType.Macro, " <string(lstr1)>"), ]; - private SeStringParameter[]? localParameters = [Versioning.GetScmVersion()]; + private SeStringParameter[]? localParameters = [Util.GetScmVersion()]; private ReadOnlySeString input; private ClientLanguage? language; private Task? validImportSheetNamesTask; @@ -196,7 +193,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget if (contentWidth != this.lastContentWidth) { var originalWidth = this.lastContentWidth != 0 ? this.lastContentWidth : contentWidth; - this.inputsWidth = (this.inputsWidth / originalWidth) * contentWidth; + this.inputsWidth = this.inputsWidth / originalWidth * contentWidth; this.lastContentWidth = contentWidth; } @@ -263,7 +260,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget using var tab = ImRaii.TabItem("Global Parameters"u8); if (!tab) return; - using var table = ImRaii.Table("GlobalParametersTable"u8, 5, TableFlags); + using var table = ImRaii.Table("GlobalParametersTable"u8, 5, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.NoSavedSettings); if (!table) return; ImGui.TableSetupColumn("Id"u8, ImGuiTableColumnFlags.WidthFixed, 40); @@ -477,25 +474,25 @@ internal class SeStringCreatorWidget : IDataWindowWidget if (ImGui.Button("Print Evaluated"u8)) { - using var rssb = new RentedSeStringBuilder(); + var sb = new LSeStringBuilder(); foreach (var entry in this.entries) { switch (entry.Type) { case TextEntryType.String: - rssb.Builder.Append(entry.Message); + sb.Append(entry.Message); break; case TextEntryType.Macro: case TextEntryType.Fixed: - rssb.Builder.AppendMacroString(entry.Message); + sb.AppendMacroString(entry.Message); break; } } var evaluated = Service<SeStringEvaluator>.Get().Evaluate( - rssb.Builder.ToReadOnlySeString(), + sb.ToReadOnlySeString(), this.localParameters, this.language); @@ -508,24 +505,24 @@ internal class SeStringCreatorWidget : IDataWindowWidget if (ImGui.Button("Copy MacroString"u8)) { - using var rssb = new RentedSeStringBuilder(); + var sb = new LSeStringBuilder(); foreach (var entry in this.entries) { switch (entry.Type) { case TextEntryType.String: - rssb.Builder.Append(entry.Message); + sb.Append(entry.Message); break; case TextEntryType.Macro: case TextEntryType.Fixed: - rssb.Builder.AppendMacroString(entry.Message); + sb.AppendMacroString(entry.Message); break; } } - ImGui.SetClipboardText(rssb.Builder.ToReadOnlySeString().ToMacroString()); + ImGui.SetClipboardText(sb.ToReadOnlySeString().ToMacroString()); } ImGui.SameLine(); @@ -546,16 +543,18 @@ internal class SeStringCreatorWidget : IDataWindowWidget ImGui.SameLine(); ImGui.SetNextItemWidth(90 * ImGuiHelpers.GlobalScale); - using var dropdown = ImRaii.Combo("##Language"u8, this.language.ToString() ?? "Language..."); - if (dropdown) + using (var dropdown = ImRaii.Combo("##Language"u8, this.language.ToString() ?? "Language...")) { - var values = Enum.GetValues<ClientLanguage>().OrderBy(lang => lang.ToString()); - foreach (var value in values) + if (dropdown) { - if (ImGui.Selectable(Enum.GetName(value), value == this.language)) + var values = Enum.GetValues<ClientLanguage>().OrderBy((ClientLanguage lang) => lang.ToString()); + foreach (var value in values) { - this.language = value; - this.UpdateInputString(); + if (ImGui.Selectable(Enum.GetName(value), value == this.language)) + { + this.language = value; + this.UpdateInputString(); + } } } } @@ -575,7 +574,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget try { var headerFile = dataManager.GameData.GetFile<ExcelHeaderFile>($"exd/{sheetName}.exh"); - if (headerFile == null || headerFile.Header.Variant != ExcelVariant.Default) + if (headerFile.Header.Variant != ExcelVariant.Default) return false; var sheet = dataManager.Excel.GetSheet<RawRow>(Language.English, sheetName); @@ -671,10 +670,11 @@ internal class SeStringCreatorWidget : IDataWindowWidget catch (Exception e) { ImGui.Text(e.Message); + return; } } - private void DrawInputs() + private unsafe void DrawInputs() { using var child = ImRaii.Child("Inputs"u8, new Vector2(this.inputsWidth, -1)); if (!child) return; @@ -690,6 +690,8 @@ internal class SeStringCreatorWidget : IDataWindowWidget var arrowUpButtonSize = this.GetIconButtonSize(FontAwesomeIcon.ArrowUp); var arrowDownButtonSize = this.GetIconButtonSize(FontAwesomeIcon.ArrowDown); + var trashButtonSize = this.GetIconButtonSize(FontAwesomeIcon.Trash); + var terminalButtonSize = this.GetIconButtonSize(FontAwesomeIcon.Terminal); var entryToRemove = -1; var entryToMoveUp = -1; @@ -706,7 +708,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget ImGui.TableNextColumn(); // Type var type = (int)entry.Type; ImGui.SetNextItemWidth(-1); - if (ImGui.Combo($"##Type{i}", ref type, TextEntryTypeOptions)) + if (ImGui.Combo($"##Type{i}", ref type, ["String", "Macro", "Fixed"])) { entry.Type = (TextEntryType)type; updateString |= true; @@ -798,26 +800,26 @@ internal class SeStringCreatorWidget : IDataWindowWidget } } - private void UpdateInputString(bool resetLocalParameters = true) + private unsafe void UpdateInputString(bool resetLocalParameters = true) { - using var rssb = new RentedSeStringBuilder(); + var sb = new LSeStringBuilder(); foreach (var entry in this.entries) { switch (entry.Type) { case TextEntryType.String: - rssb.Builder.Append(entry.Message); + sb.Append(entry.Message); break; case TextEntryType.Macro: case TextEntryType.Fixed: - rssb.Builder.AppendMacroString(entry.Message); + sb.AppendMacroString(entry.Message); break; } } - this.input = rssb.Builder.ToReadOnlySeString(); + this.input = sb.ToReadOnlySeString(); if (resetLocalParameters) this.localParameters = null; @@ -996,9 +998,10 @@ internal class SeStringCreatorWidget : IDataWindowWidget } } - using var rssb = new RentedSeStringBuilder(); - rssb.Builder.AppendIcon(iconId); - ImGuiHelpers.SeStringWrapped(rssb.Builder.ToArray()); + var builder = LSeStringBuilder.SharedPool.Get(); + builder.AppendIcon(iconId); + ImGuiHelpers.SeStringWrapped(builder.ToArray()); + LSeStringBuilder.SharedPool.Return(builder); ImGui.SameLine(); } @@ -1022,14 +1025,14 @@ internal class SeStringCreatorWidget : IDataWindowWidget if (macroCode is MacroCode.JaNoun or MacroCode.EnNoun or MacroCode.DeNoun or MacroCode.FrNoun && exprIdx == 1) { - var macroLanguage = macroCode switch + var language = macroCode switch { MacroCode.JaNoun => ClientLanguage.Japanese, MacroCode.DeNoun => ClientLanguage.German, MacroCode.FrNoun => ClientLanguage.French, _ => ClientLanguage.English, }; - var articleTypeEnumType = macroLanguage switch + var articleTypeEnumType = language switch { ClientLanguage.Japanese => typeof(JapaneseArticleType), ClientLanguage.German => typeof(GermanArticleType), @@ -1208,10 +1211,12 @@ internal class SeStringCreatorWidget : IDataWindowWidget if (expressionType == (int)ExpressionType.LocalNumber) { parameters[index] = new SeStringParameter(0); + return; } else if (expressionType == (int)ExpressionType.LocalString) { parameters[index] = new SeStringParameter(string.Empty); + return; } } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringRendererTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringRendererTestWidget.cs index 0e4e5792d..7ff5a63be 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringRendererTestWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringRendererTestWidget.cs @@ -1,6 +1,5 @@ using System.Numerics; using System.Text; -using System.Threading.Tasks; using Dalamud.Bindings.ImGui; using Dalamud.Data; @@ -10,16 +9,11 @@ using Dalamud.Interface.ImGuiSeStringRenderer; using Dalamud.Interface.ImGuiSeStringRenderer.Internal; using Dalamud.Interface.Textures.Internal; using Dalamud.Interface.Utility; -using Dalamud.Interface.Utility.Internal; -using Dalamud.Interface.Utility.Raii; using Dalamud.Storage.Assets; using Dalamud.Utility; - using FFXIVClientStructs.FFXIV.Component.GUI; - using Lumina.Excel.Sheets; using Lumina.Text; -using Lumina.Text.Parse; using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; @@ -30,7 +24,7 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// </summary> internal unsafe class SeStringRendererTestWidget : IDataWindowWidget { - private static readonly string[] ThemeNames = ["Dark", "Light", "Classic FF", "Clear Blue", "Clear White", "Clear Green"]; + private static readonly string[] ThemeNames = ["Dark", "Light", "Classic FF", "Clear Blue"]; private ImVectorWrapper<byte> testStringBuffer; private string testString = string.Empty; private ReadOnlySeString? logkind; @@ -62,11 +56,11 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget /// <inheritdoc/> public void Draw() { - var t2 = ImGui.ColorConvertU32ToFloat4(this.style.Color ??= ImGui.GetColorU32(ImGuiCol.Text)); + var t2 = ImGui.ColorConvertU32ToFloat4(this.style.Color ?? ImGui.GetColorU32(ImGuiCol.Text)); if (ImGui.ColorEdit4("Color", ref t2)) this.style.Color = ImGui.ColorConvertFloat4ToU32(t2); - t2 = ImGui.ColorConvertU32ToFloat4(this.style.EdgeColor ??= 0xFF000000u); + t2 = ImGui.ColorConvertU32ToFloat4(this.style.EdgeColor ?? 0xFF000000u); if (ImGui.ColorEdit4("Edge Color", ref t2)) this.style.EdgeColor = ImGui.ColorConvertFloat4ToU32(t2); @@ -75,27 +69,27 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget if (ImGui.Checkbox("Forced"u8, ref t)) this.style.ForceEdgeColor = t; - t2 = ImGui.ColorConvertU32ToFloat4(this.style.ShadowColor ??= 0xFF000000u); - if (ImGui.ColorEdit4("Shadow Color"u8, ref t2)) + t2 = ImGui.ColorConvertU32ToFloat4(this.style.ShadowColor ?? 0xFF000000u); + if (ImGui.ColorEdit4("Shadow Color", ref t2)) this.style.ShadowColor = ImGui.ColorConvertFloat4ToU32(t2); - t2 = ImGui.ColorConvertU32ToFloat4(this.style.LinkHoverBackColor ??= ImGui.GetColorU32(ImGuiCol.ButtonHovered)); - if (ImGui.ColorEdit4("Link Hover Color"u8, ref t2)) + t2 = ImGui.ColorConvertU32ToFloat4(this.style.LinkHoverBackColor ?? ImGui.GetColorU32(ImGuiCol.ButtonHovered)); + if (ImGui.ColorEdit4("Link Hover Color", ref t2)) this.style.LinkHoverBackColor = ImGui.ColorConvertFloat4ToU32(t2); - t2 = ImGui.ColorConvertU32ToFloat4(this.style.LinkActiveBackColor ??= ImGui.GetColorU32(ImGuiCol.ButtonActive)); - if (ImGui.ColorEdit4("Link Active Color"u8, ref t2)) + t2 = ImGui.ColorConvertU32ToFloat4(this.style.LinkActiveBackColor ?? ImGui.GetColorU32(ImGuiCol.ButtonActive)); + if (ImGui.ColorEdit4("Link Active Color", ref t2)) this.style.LinkActiveBackColor = ImGui.ColorConvertFloat4ToU32(t2); - var t3 = this.style.LineHeight ??= 1f; + var t3 = this.style.LineHeight ?? 1f; if (ImGui.DragFloat("Line Height"u8, ref t3, 0.01f, 0.4f, 3f, "%.02f")) this.style.LineHeight = t3; - t3 = this.style.Opacity ??= 1f; + t3 = this.style.Opacity ?? ImGui.GetStyle().Alpha; if (ImGui.DragFloat("Opacity"u8, ref t3, 0.005f, 0f, 1f, "%.02f")) this.style.Opacity = t3; - t3 = this.style.EdgeStrength ??= 0.25f; + t3 = this.style.EdgeStrength ?? 0.25f; if (ImGui.DragFloat("Edge Strength"u8, ref t3, 0.005f, 0f, 1f, "%.02f")) this.style.EdgeStrength = t3; @@ -120,11 +114,9 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget ImGui.SameLine(); var t4 = this.style.ThemeIndex ?? AtkStage.Instance()->AtkUIColorHolder->ActiveColorThemeType; - using (ImRaii.ItemWidth(ImGui.CalcTextSize("WWWWWWWWWWWWWW"u8).X)) - { - if (ImGui.Combo("##theme", ref t4, ThemeNames)) - this.style.ThemeIndex = t4; - } + ImGui.PushItemWidth(ImGui.CalcTextSize("WWWWWWWWWWWWWW"u8).X); + if (ImGui.Combo("##theme", ref t4, ThemeNames)) + this.style.ThemeIndex = t4; ImGui.SameLine(); t = this.style.LinkUnderlineThickness > 0f; @@ -182,32 +174,17 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget ImGuiHelpers.SeStringWrapped(this.logkind.Value.Data.Span, this.style); } - if (ImGui.CollapsingHeader("Draw into drawlist")) - { - ImGuiHelpers.ScaledDummy(100); - ImGui.SetCursorScreenPos(ImGui.GetItemRectMin() + ImGui.GetStyle().FramePadding); - var clipMin = ImGui.GetItemRectMin() + ImGui.GetStyle().FramePadding; - var clipMax = ImGui.GetItemRectMax() - ImGui.GetStyle().FramePadding; - clipMin.Y = MathF.Max(clipMin.Y, ImGui.GetWindowPos().Y); - clipMax.Y = MathF.Min(clipMax.Y, ImGui.GetWindowPos().Y + ImGui.GetWindowHeight()); - - var dl = ImGui.GetWindowDrawList(); - dl.PushClipRect(clipMin, clipMax); - ImGuiHelpers.CompileSeStringWrapped( - "<icon(1)>Test test<icon(1)>", - new SeStringDrawParams { Color = 0xFFFFFFFF, WrapWidth = float.MaxValue, TargetDrawList = dl }); - dl.PopClipRect(); - } - if (ImGui.CollapsingHeader("Addon Table"u8)) { - using var table = ImRaii.Table("Addon Sheet"u8, 3); - if (table.Success) + if (ImGui.BeginTable("Addon Sheet"u8, 3)) { ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableSetupColumn("Row ID"u8, ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("0000000"u8).X); ImGui.TableSetupColumn("Text"u8, ImGuiTableColumnFlags.WidthStretch); - ImGui.TableSetupColumn("Misc"u8, ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("AAAAAAAAAAAAAAAAA"u8).X); + ImGui.TableSetupColumn( + "Misc"u8, + ImGuiTableColumnFlags.WidthFixed, + ImGui.CalcTextSize("AAAAAAAAAAAAAAAAA"u8).X); ImGui.TableHeadersRow(); var addon = Service<DataManager>.GetNullable()?.GetExcelSheet<Addon>() ?? @@ -222,7 +199,7 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget var row = addon.GetRowAt(i); ImGui.TableNextRow(); - using var pushedId = ImRaii.PushId(i); + ImGui.PushID(i); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); @@ -234,11 +211,14 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget ImGui.TableNextColumn(); if (ImGui.Button("Print to Chat"u8)) - Service<ChatGui>.Get().Print(row.Text); + Service<ChatGui>.Get().Print(row.Text.ToDalamudString()); + + ImGui.PopID(); } } clipper.Destroy(); + ImGui.EndTable(); } } @@ -255,28 +235,9 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget if (ImGui.Button("Print to Chat Log"u8)) { - Service<ChatGui>.Get().Print(Service<SeStringRenderer>.Get().CompileAndCache(this.testString)); - } - - ImGui.SameLine(); - - if (ImGui.Button("Copy as Image")) - { - _ = Service<DevTextureSaveMenu>.Get().ShowTextureSaveMenuAsync( - this.DisplayName, - $"From {nameof(SeStringRendererTestWidget)}", - Task.FromResult( - Service<TextureManager>.Get().CreateTextureFromSeString( - ReadOnlySeString.FromMacroString( - this.testString, - new(ExceptionMode: MacroStringParseExceptionMode.EmbedError)), - this.style with - { - Font = ImGui.GetFont(), - FontSize = ImGui.GetFontSize(), - WrapWidth = ImGui.GetContentRegionAvail().X, - ThemeIndex = AtkStage.Instance()->AtkUIColorHolder->ActiveColorThemeType, - }))); + Service<ChatGui>.Get().Print( + Game.Text.SeStringHandling.SeString.Parse( + Service<SeStringRenderer>.Get().CompileAndCache(this.testString).Data.Span)); } ImGuiHelpers.ScaledDummy(3); @@ -310,7 +271,6 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget var len = this.testStringBuffer.StorageSpan.IndexOf((byte)0); if (len + 4 >= this.testStringBuffer.Capacity) this.testStringBuffer.EnsureCapacityExponential(len + 4); - if (len < this.testStringBuffer.Capacity) { this.testStringBuffer.LengthUnsafe = len; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ServicesWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ServicesWidget.cs index a4351248b..78ea6d233 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ServicesWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ServicesWidget.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Reflection; @@ -16,15 +16,15 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// </summary> internal class ServicesWidget : IDataWindowWidget { - private readonly Dictionary<ServiceDependencyNode, Vector4> nodeRects = []; - private readonly HashSet<Type> selectedNodes = []; - private readonly HashSet<Type> tempRelatedNodes = []; + private readonly Dictionary<ServiceDependencyNode, Vector4> nodeRects = new(); + private readonly HashSet<Type> selectedNodes = new(); + private readonly HashSet<Type> tempRelatedNodes = new(); private bool includeUnloadDependencies; private List<List<ServiceDependencyNode>>? dependencyNodes; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["services"]; + public string[]? CommandShortcuts { get; init; } = { "services" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Service Container"; @@ -66,10 +66,11 @@ internal class ServicesWidget : IDataWindowWidget var margin = ImGui.CalcTextSize("W\nW\nW"u8); var rowHeight = cellPad.Y * 3; var width = ImGui.GetContentRegionAvail().X; - var childSize = new Vector2(width, (this.dependencyNodes.Count * (rowHeight + margin.Y)) + cellPad.Y); - - using var child = ImRaii.Child("dependency-graph"u8, childSize, false, ImGuiWindowFlags.HorizontalScrollbar); - if (child.Success) + if (ImGui.BeginChild( + "dependency-graph"u8, + new(width, (this.dependencyNodes.Count * (rowHeight + margin.Y)) + cellPad.Y), + false, + ImGuiWindowFlags.HorizontalScrollbar)) { const uint rectBaseBorderColor = 0xFFFFFFFF; const uint rectHoverFillColor = 0xFF404040; @@ -117,8 +118,10 @@ internal class ServicesWidget : IDataWindowWidget hoveredNode = node; if (ImGui.IsMouseClicked(ImGuiMouseButton.Left)) { - if (!this.selectedNodes.Add(node.Type)) + if (this.selectedNodes.Contains(node.Type)) this.selectedNodes.Remove(node.Type); + else + this.selectedNodes.Add(node.Type); } } @@ -192,11 +195,13 @@ internal class ServicesWidget : IDataWindowWidget ImGui.SetTooltip(node.BlockingReason); ImGui.SetCursorPos((new Vector2(rc.X, rc.Y) - pos) + ((cellSize - textSize) / 2)); - using var pushedStyle = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero); + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero); ImGui.Text(node.DisplayedName); ImGui.SameLine(); - using var pushedColor = ImRaii.PushColor(ImGuiCol.Text, node.TypeSuffixColor); + ImGui.PushStyleColor(ImGuiCol.Text, node.TypeSuffixColor); ImGui.Text(node.TypeSuffix); + ImGui.PopStyleVar(); + ImGui.PopStyleColor(); } } @@ -228,6 +233,7 @@ internal class ServicesWidget : IDataWindowWidget ImGui.SetCursorPos(default); ImGui.Dummy(new(maxRowWidth, this.dependencyNodes.Count * rowHeight)); + ImGui.EndChild(); } } @@ -274,9 +280,9 @@ internal class ServicesWidget : IDataWindowWidget private class ServiceDependencyNode { - private readonly List<ServiceDependencyNode> parents = []; - private readonly List<ServiceDependencyNode> children = []; - private readonly List<ServiceDependencyNode> invalidParents = []; + private readonly List<ServiceDependencyNode> parents = new(); + private readonly List<ServiceDependencyNode> children = new(); + private readonly List<ServiceDependencyNode> invalidParents = new(); private ServiceDependencyNode(Type t) { @@ -364,7 +370,7 @@ internal class ServicesWidget : IDataWindowWidget foreach (var n in CreateTree(includeUnloadDependencies)) { while (res.Count <= n.Level) - res.Add([]); + res.Add(new()); res[n.Level].Add(n); } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/StartInfoWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/StartInfoWidget.cs index c0c38da24..7fb2cc2bf 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/StartInfoWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/StartInfoWidget.cs @@ -1,5 +1,4 @@ -using Dalamud.Bindings.ImGui; - +using Dalamud.Bindings.ImGui; using Newtonsoft.Json; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -10,7 +9,7 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; internal class StartInfoWidget : IDataWindowWidget { /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["startinfo"]; + public string[]? CommandShortcuts { get; init; } = { "startinfo" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Start Info"; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs index 2e52d7586..6caf3286d 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs @@ -14,7 +14,7 @@ internal class TargetWidget : IDataWindowWidget private bool resolveGameData; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["target"]; + public string[]? CommandShortcuts { get; init; } = { "target" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Target"; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TaskSchedulerWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TaskSchedulerWidget.cs index 7f9606c4f..cd72d751e 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/TaskSchedulerWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TaskSchedulerWidget.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Net.Http; using System.Reflection; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -16,7 +17,6 @@ using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Logging.Internal; using Dalamud.Utility; - using Serilog; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -35,7 +35,7 @@ internal class TaskSchedulerWidget : IDataWindowWidget private CancellationTokenSource taskSchedulerCancelSource = new(); /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["tasksched", "taskscheduler"]; + public string[]? CommandShortcuts { get; init; } = { "tasksched", "taskscheduler" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Task Scheduler"; @@ -266,7 +266,8 @@ internal class TaskSchedulerWidget : IDataWindowWidget ImGui.Text($"{this.downloadState.Downloaded:##,###}/{this.downloadState.Total:##,###} ({this.downloadState.Percentage:0.00}%)"); - using var disabled = ImRaii.Disabled(this.downloadTask?.IsCompleted is false || this.localPath[0] == 0); + using var disabled = + ImRaii.Disabled(this.downloadTask?.IsCompleted is false || this.localPath[0] == 0); ImGui.AlignTextToFramePadding(); ImGui.Text("Download"u8); ImGui.SameLine(); @@ -387,19 +388,27 @@ internal class TaskSchedulerWidget : IDataWindowWidget if (task.Task == null) subTime = task.FinishTime; - using var pushedColor = task.Status switch + switch (task.Status) { - TaskStatus.Created or TaskStatus.WaitingForActivation or TaskStatus.WaitingToRun - => ImRaii.PushColor(ImGuiCol.Header, ImGuiColors.DalamudGrey), - TaskStatus.Running or TaskStatus.WaitingForChildrenToComplete - => ImRaii.PushColor(ImGuiCol.Header, ImGuiColors.ParsedBlue), - TaskStatus.RanToCompletion - => ImRaii.PushColor(ImGuiCol.Header, ImGuiColors.ParsedGreen), - TaskStatus.Canceled or TaskStatus.Faulted - => ImRaii.PushColor(ImGuiCol.Header, ImGuiColors.DalamudRed), - - _ => throw new ArgumentOutOfRangeException(), - }; + case TaskStatus.Created: + case TaskStatus.WaitingForActivation: + case TaskStatus.WaitingToRun: + ImGui.PushStyleColor(ImGuiCol.Header, ImGuiColors.DalamudGrey); + break; + case TaskStatus.Running: + case TaskStatus.WaitingForChildrenToComplete: + ImGui.PushStyleColor(ImGuiCol.Header, ImGuiColors.ParsedBlue); + break; + case TaskStatus.RanToCompletion: + ImGui.PushStyleColor(ImGuiCol.Header, ImGuiColors.ParsedGreen); + break; + case TaskStatus.Canceled: + case TaskStatus.Faulted: + ImGui.PushStyleColor(ImGuiCol.Header, ImGuiColors.DalamudRed); + break; + default: + throw new ArgumentOutOfRangeException(); + } if (ImGui.CollapsingHeader($"#{task.Id} - {task.Status} {(subTime - task.StartTime).TotalMilliseconds}ms###task{i}")) { @@ -409,7 +418,8 @@ internal class TaskSchedulerWidget : IDataWindowWidget { try { - var cancelFunc = typeof(Task).GetMethod("InternalCancel", BindingFlags.NonPublic | BindingFlags.Instance); + var cancelFunc = + typeof(Task).GetMethod("InternalCancel", BindingFlags.NonPublic | BindingFlags.Instance); cancelFunc?.Invoke(task, null); } catch (Exception ex) @@ -420,7 +430,7 @@ internal class TaskSchedulerWidget : IDataWindowWidget ImGuiHelpers.ScaledDummy(10); - ImGui.Text(task.StackTrace?.ToString() ?? "Null StackTrace"); + ImGui.Text(task.StackTrace?.ToString()); if (task.Exception != null) { @@ -433,6 +443,8 @@ internal class TaskSchedulerWidget : IDataWindowWidget { task.IsBeingViewed = false; } + + ImGui.PopStyleColor(1); } this.fileDialogManager.Draw(); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs index 866640996..52fa0e822 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; @@ -14,11 +14,9 @@ using Dalamud.Interface.Textures.Internal.SharedImmediateTextures; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Internal; -using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin.Services; using Dalamud.Storage.Assets; using Dalamud.Utility; - using TerraFX.Interop.DirectX; using TextureManager = Dalamud.Interface.Textures.Internal.TextureManager; @@ -30,10 +28,6 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// </summary> internal class TexWidget : IDataWindowWidget { - private const ImGuiTableFlags TableFlags = ImGuiTableFlags.Sortable | ImGuiTableFlags.SortTristate | ImGuiTableFlags.SortMulti | - ImGuiTableFlags.Reorderable | ImGuiTableFlags.Resizable | ImGuiTableFlags.NoBordersInBodyUntilResize | - ImGuiTableFlags.NoSavedSettings; - // TODO: move tracking implementation to PluginStats where applicable, // and show stats over there instead of TexWidget. private static readonly Dictionary< @@ -49,12 +43,12 @@ internal class TexWidget : IDataWindowWidget [DrawBlameTableColumnUserId.NativeAddress] = static x => x.ResourceAddress, }; - private readonly List<TextureEntry> addedTextures = []; + private readonly List<TextureEntry> addedTextures = new(); private string allLoadedTexturesTableName = "##table"; private string iconId = "18"; private bool hiRes = true; - private bool hq; + private bool hq = false; private string inputTexPath = string.Empty; private string inputFilePath = string.Empty; private Assembly[]? inputManifestResourceAssemblyCandidates; @@ -89,7 +83,7 @@ internal class TexWidget : IDataWindowWidget } /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["tex", "texture"]; + public string[]? CommandShortcuts { get; init; } = { "tex", "texture" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Tex"; @@ -143,42 +137,48 @@ internal class TexWidget : IDataWindowWidget conf.QueueSave(); } - lock (this.textureManager.BlameTracker) + var allBlames = this.textureManager.BlameTracker; + lock (allBlames) { - using var pushedId = ImRaii.PushId("blames"u8); - var allBlames = this.textureManager.BlameTracker; + ImGui.PushID("blames"u8); var sizeSum = allBlames.Sum(static x => Math.Max(0, x.RawSpecs.EstimatedBytes)); - if (ImGui.CollapsingHeader($"All Loaded Textures: {allBlames.Count:n0} ({Util.FormatBytes(sizeSum)})###header")) + if (ImGui.CollapsingHeader( + $"All Loaded Textures: {allBlames.Count:n0} ({Util.FormatBytes(sizeSum)})###header")) this.DrawBlame(allBlames); + ImGui.PopID(); } - using (ImRaii.PushId("loadedGameTextures"u8)) - { - if (ImGui.CollapsingHeader($"Loaded Game Textures: {this.textureManager.Shared.ForDebugGamePathTextures.Count:n0}###header")) - this.DrawLoadedTextures(this.textureManager.Shared.ForDebugGamePathTextures); - } + ImGui.PushID("loadedGameTextures"u8); + if (ImGui.CollapsingHeader( + $"Loaded Game Textures: {this.textureManager.Shared.ForDebugGamePathTextures.Count:n0}###header")) + this.DrawLoadedTextures(this.textureManager.Shared.ForDebugGamePathTextures); + ImGui.PopID(); - using (ImRaii.PushId("loadedFileTextures"u8)) - { - if (ImGui.CollapsingHeader($"Loaded File Textures: {this.textureManager.Shared.ForDebugFileSystemTextures.Count:n0}###header")) - this.DrawLoadedTextures(this.textureManager.Shared.ForDebugFileSystemTextures); - } + ImGui.PushID("loadedFileTextures"u8); + if (ImGui.CollapsingHeader( + $"Loaded File Textures: {this.textureManager.Shared.ForDebugFileSystemTextures.Count:n0}###header")) + this.DrawLoadedTextures(this.textureManager.Shared.ForDebugFileSystemTextures); + ImGui.PopID(); - using (ImRaii.PushId("loadedManifestResourceTextures"u8)) - { - if (ImGui.CollapsingHeader($"Loaded Manifest Resource Textures: {this.textureManager.Shared.ForDebugManifestResourceTextures.Count:n0}###header")) - this.DrawLoadedTextures(this.textureManager.Shared.ForDebugManifestResourceTextures); - } + ImGui.PushID("loadedManifestResourceTextures"u8); + if (ImGui.CollapsingHeader( + $"Loaded Manifest Resource Textures: {this.textureManager.Shared.ForDebugManifestResourceTextures.Count:n0}###header")) + this.DrawLoadedTextures(this.textureManager.Shared.ForDebugManifestResourceTextures); + ImGui.PopID(); lock (this.textureManager.Shared.ForDebugInvalidatedTextures) { - using var pushedId = ImRaii.PushId("invalidatedTextures"u8); - if (ImGui.CollapsingHeader($"Invalidated: {this.textureManager.Shared.ForDebugInvalidatedTextures.Count:n0}###header")) + ImGui.PushID("invalidatedTextures"u8); + if (ImGui.CollapsingHeader( + $"Invalidated: {this.textureManager.Shared.ForDebugInvalidatedTextures.Count:n0}###header")) + { this.DrawLoadedTextures(this.textureManager.Shared.ForDebugInvalidatedTextures); + } + + ImGui.PopID(); } - var textHeightSpacing = new Vector2(ImGui.GetTextLineHeightWithSpacing()); - ImGui.Dummy(textHeightSpacing); + ImGui.Dummy(new(ImGui.GetTextLineHeightWithSpacing())); if (!this.textureManager.HasClipboardImage()) { @@ -191,53 +191,59 @@ internal class TexWidget : IDataWindowWidget if (ImGui.CollapsingHeader(nameof(ITextureProvider.GetFromGameIcon))) { - using var pushedId = ImRaii.PushId(nameof(this.DrawGetFromGameIcon)); + ImGui.PushID(nameof(this.DrawGetFromGameIcon)); this.DrawGetFromGameIcon(); + ImGui.PopID(); } if (ImGui.CollapsingHeader(nameof(ITextureProvider.GetFromGame))) { - using var pushedId = ImRaii.PushId(nameof(this.DrawGetFromGame)); + ImGui.PushID(nameof(this.DrawGetFromGame)); this.DrawGetFromGame(); + ImGui.PopID(); } if (ImGui.CollapsingHeader(nameof(ITextureProvider.GetFromFile))) { - using var pushedId = ImRaii.PushId(nameof(this.DrawGetFromFile)); + ImGui.PushID(nameof(this.DrawGetFromFile)); this.DrawGetFromFile(); + ImGui.PopID(); } if (ImGui.CollapsingHeader(nameof(ITextureProvider.GetFromManifestResource))) { - using var pushedId = ImRaii.PushId(nameof(this.DrawGetFromManifestResource)); + ImGui.PushID(nameof(this.DrawGetFromManifestResource)); this.DrawGetFromManifestResource(); + ImGui.PopID(); } if (ImGui.CollapsingHeader(nameof(ITextureProvider.CreateFromImGuiViewportAsync))) { - using var pushedId = ImRaii.PushId(nameof(this.DrawCreateFromImGuiViewportAsync)); + ImGui.PushID(nameof(this.DrawCreateFromImGuiViewportAsync)); this.DrawCreateFromImGuiViewportAsync(); + ImGui.PopID(); } if (ImGui.CollapsingHeader("UV"u8)) { - using var pushedId = ImRaii.PushId(nameof(this.DrawUvInput)); + ImGui.PushID(nameof(this.DrawUvInput)); this.DrawUvInput(); + ImGui.PopID(); } if (ImGui.CollapsingHeader($"CropCopy##{nameof(this.DrawExistingTextureModificationArgs)}")) { - using var pushedId = ImRaii.PushId(nameof(this.DrawExistingTextureModificationArgs)); + ImGui.PushID(nameof(this.DrawExistingTextureModificationArgs)); this.DrawExistingTextureModificationArgs(); + ImGui.PopID(); } - ImGui.Dummy(textHeightSpacing); + ImGui.Dummy(new(ImGui.GetTextLineHeightWithSpacing())); Action? runLater = null; foreach (var t in this.addedTextures) { - using var pushedId = ImRaii.PushId(t.Id); - + ImGui.PushID(t.Id); if (ImGui.CollapsingHeader($"Tex #{t.Id} {t}###header", ImGuiTreeNodeFlags.DefaultOpen)) { if (ImGui.Button("X"u8)) @@ -300,12 +306,12 @@ internal class TexWidget : IDataWindowWidget pres->Release(); ImGui.Text($"RC: Resource({rcres})/View({rcsrv})"); - ImGui.Text($"{source.Width} x {source.Height} | {source}"); + ImGui.Text(source.ToString()); } else { - ImGui.Text("RC: -"); - ImGui.Text(string.Empty); + ImGui.Text("RC: -"u8); + ImGui.Text(" "u8); } } @@ -329,15 +335,13 @@ internal class TexWidget : IDataWindowWidget ImGui.Text(e.ToString()); } } + + ImGui.PopID(); } runLater?.Invoke(); } - /// <summary>Adds a texture wrap for debug display purposes.</summary> - /// <param name="textureTask">Task returning a texture.</param> - public void AddTexture(Task<IDalamudTextureWrap> textureTask) => this.addedTextures.Add(new(Api10: textureTask)); - private unsafe void DrawBlame(List<TextureManager.IBlameableDalamudTextureWrap> allBlames) { var im = Service<InterfaceManager>.Get(); @@ -348,16 +352,18 @@ internal class TexWidget : IDataWindowWidget if (ImGui.Button("Reset Columns"u8)) this.allLoadedTexturesTableName = "##table" + Environment.TickCount64; - using var table = ImRaii.Table(this.allLoadedTexturesTableName, (int)DrawBlameTableColumnUserId.ColumnCount, TableFlags); - if (!table.Success) + if (!ImGui.BeginTable( + this.allLoadedTexturesTableName, + (int)DrawBlameTableColumnUserId.ColumnCount, + ImGuiTableFlags.Sortable | ImGuiTableFlags.SortTristate | ImGuiTableFlags.SortMulti | + ImGuiTableFlags.Reorderable | ImGuiTableFlags.Resizable | ImGuiTableFlags.NoBordersInBodyUntilResize | + ImGuiTableFlags.NoSavedSettings)) return; const int numIcons = 1; float iconWidths; using (im.IconFontHandle?.Push()) - { iconWidths = ImGui.CalcTextSize(FontAwesomeIcon.Save.ToIconString()).X; - } ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableSetupColumn( @@ -452,8 +458,7 @@ internal class TexWidget : IDataWindowWidget { var wrap = allBlames[i]; ImGui.TableNextRow(); - - using var pushedId = ImRaii.PushId(i); + ImGui.PushID(i); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); @@ -470,8 +475,9 @@ internal class TexWidget : IDataWindowWidget if (ImGui.IsItemHovered()) { - using var tooltip = ImRaii.Tooltip(); + ImGui.BeginTooltip(); ImGui.Image(wrap.Handle, wrap.Size); + ImGui.EndTooltip(); } ImGui.TableNextColumn(); @@ -493,19 +499,21 @@ internal class TexWidget : IDataWindowWidget ImGui.TableNextColumn(); lock (wrap.OwnerPlugins) this.TextColumnCopiable(string.Join(", ", wrap.OwnerPlugins.Select(static x => x.Name)), false, true); + + ImGui.PopID(); } } clipper.Destroy(); + ImGui.EndTable(); ImGuiHelpers.ScaledDummy(10); } - private void DrawLoadedTextures(ICollection<SharedImmediateTexture> textures) + private unsafe void DrawLoadedTextures(ICollection<SharedImmediateTexture> textures) { var im = Service<InterfaceManager>.Get(); - using var table = ImRaii.Table("##table"u8, 6); - if (!table.Success) + if (!ImGui.BeginTable("##table"u8, 6)) return; const int numIcons = 4; @@ -563,7 +571,7 @@ internal class TexWidget : IDataWindowWidget } var remain = texture.SelfReferenceExpiresInForDebug; - using var pushedId = ImRaii.PushId(row); + ImGui.PushID(row); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); @@ -590,26 +598,28 @@ internal class TexWidget : IDataWindowWidget if (ImGui.IsItemHovered() && texture.GetWrapOrDefault(null) is { } immediate) { - using var tooltip = ImRaii.Tooltip(); + ImGui.BeginTooltip(); ImGui.Image(immediate.Handle, immediate.Size); + ImGui.EndTooltip(); } ImGui.SameLine(); if (ImGuiComponents.IconButton(FontAwesomeIcon.Sync)) - this.textureManager.InvalidatePaths([texture.SourcePathForDebug]); - + this.textureManager.InvalidatePaths(new[] { texture.SourcePathForDebug }); if (ImGui.IsItemHovered()) ImGui.SetTooltip($"Call {nameof(ITextureSubstitutionProvider.InvalidatePaths)}."); ImGui.SameLine(); - using (ImRaii.Disabled(remain <= 0)) - { - if (ImGuiComponents.IconButton(FontAwesomeIcon.Trash)) - texture.ReleaseSelfReference(true); + if (remain <= 0) + ImGui.BeginDisabled(); + if (ImGuiComponents.IconButton(FontAwesomeIcon.Trash)) + texture.ReleaseSelfReference(true); + if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) + ImGui.SetTooltip("Release self-reference immediately."u8); + if (remain <= 0) + ImGui.EndDisabled(); - if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) - ImGui.SetTooltip("Release self-reference immediately."u8); - } + ImGui.PopID(); } if (!valid) @@ -618,6 +628,7 @@ internal class TexWidget : IDataWindowWidget } clipper.Destroy(); + ImGui.EndTable(); ImGuiHelpers.ScaledDummy(10); } @@ -736,7 +747,10 @@ internal class TexWidget : IDataWindowWidget { ImGui.SameLine(); if (ImGui.Button("Load File (Async)"u8)) - this.addedTextures.Add(new(Api10: this.textureManager.Shared.GetFromManifestResource(assembly, name).RentAsync())); + { + this.addedTextures.Add( + new(Api10: this.textureManager.Shared.GetFromManifestResource(assembly, name).RentAsync())); + } ImGui.SameLine(); if (ImGui.Button("Load File (Immediate)"u8)) @@ -749,20 +763,21 @@ internal class TexWidget : IDataWindowWidget private void DrawCreateFromImGuiViewportAsync() { var viewports = ImGui.GetPlatformIO().Viewports; - using (var combo = ImRaii.Combo(nameof(this.viewportTextureArgs.ViewportId), $"{this.viewportIndexInt}. {viewports[this.viewportIndexInt].ID:X08}")) + if (ImGui.BeginCombo( + nameof(this.viewportTextureArgs.ViewportId), + $"{this.viewportIndexInt}. {viewports[this.viewportIndexInt].ID:X08}")) { - if (combo.Success) + for (var i = 0; i < viewports.Size; i++) { - for (var i = 0; i < viewports.Size; i++) + var sel = this.viewportIndexInt == i; + if (ImGui.Selectable($"#{i}: {viewports[i].ID:X08}", ref sel)) { - var sel = this.viewportIndexInt == i; - if (ImGui.Selectable($"#{i}: {viewports[i].ID:X08}", ref sel)) - { - this.viewportIndexInt = i; - ImGui.SetItemDefaultFocus(); - } + this.viewportIndexInt = i; + ImGui.SetItemDefaultFocus(); } } + + ImGui.EndCombo(); } var b = this.viewportTextureArgs.KeepTransparency; @@ -824,12 +839,17 @@ internal class TexWidget : IDataWindowWidget } this.supportedRenderTargetFormatNames ??= this.supportedRenderTargetFormats.Select(Enum.GetName).ToArray(); - ImGui.Combo(nameof(this.textureModificationArgs.DxgiFormat), ref this.renderTargetChoiceInt, this.supportedRenderTargetFormatNames); + ImGui.Combo( + nameof(this.textureModificationArgs.DxgiFormat), + ref this.renderTargetChoiceInt, + this.supportedRenderTargetFormatNames); Span<int> wh = stackalloc int[2]; wh[0] = this.textureModificationArgs.NewWidth; wh[1] = this.textureModificationArgs.NewHeight; - if (ImGui.InputInt($"{nameof(this.textureModificationArgs.NewWidth)}/{nameof(this.textureModificationArgs.NewHeight)}", wh)) + if (ImGui.InputInt( + $"{nameof(this.textureModificationArgs.NewWidth)}/{nameof(this.textureModificationArgs.NewHeight)}", + wh)) { this.textureModificationArgs.NewWidth = wh[0]; this.textureModificationArgs.NewHeight = wh[1]; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ToastWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ToastWidget.cs index 6be0a3a85..5e5a077c3 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ToastWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ToastWidget.cs @@ -20,7 +20,7 @@ internal class ToastWidget : IDataWindowWidget private bool questToastCheckmark; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["toast"]; + public string[]? CommandShortcuts { get; init; } = { "toast" }; /// <inheritdoc/> public string DisplayName { get; init; } = "Toast"; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs index bc6e5376c..e52a291ef 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs @@ -7,8 +7,6 @@ using Dalamud.Data; using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification.Internal; using Dalamud.Interface.ImGuiSeStringRenderer.Internal; -using Dalamud.Interface.Utility.Raii; - using Lumina.Excel.Sheets; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -34,7 +32,7 @@ internal class UiColorWidget : IDataWindowWidget } /// <inheritdoc/> - public void Draw() + public unsafe void Draw() { var colors = Service<DataManager>.GetNullable()?.GetExcelSheet<UIColor>() ?? throw new InvalidOperationException("UIColor sheet not loaded."); @@ -46,9 +44,7 @@ internal class UiColorWidget : IDataWindowWidget "<edgecolor(0xEEEEFF)><color(0x0000FF)>BB<color(stackcolor)><edgecolor(stackcolor)>.<br>" + "· Click on a color to copy the color code.<br>" + "· Hover on a color to preview the text with edge, when the next color has been used together."); - - using var table = ImRaii.Table("UIColor"u8, 7); - if (!table.Success) + if (!ImGui.BeginTable("UIColor"u8, 5)) return; ImGui.TableSetupScrollFreeze(0, 1); @@ -66,8 +62,6 @@ internal class UiColorWidget : IDataWindowWidget ImGui.TableSetupColumn("Light"u8, ImGuiTableColumnFlags.WidthFixed, colorw); ImGui.TableSetupColumn("Classic FF"u8, ImGuiTableColumnFlags.WidthFixed, colorw); ImGui.TableSetupColumn("Clear Blue"u8, ImGuiTableColumnFlags.WidthFixed, colorw); - ImGui.TableSetupColumn("Clear White"u8, ImGuiTableColumnFlags.WidthFixed, colorw); - ImGui.TableSetupColumn("Clear Green"u8, ImGuiTableColumnFlags.WidthFixed, colorw); ImGui.TableHeadersRow(); var clipper = ImGui.ImGuiListClipper(); @@ -97,61 +91,45 @@ internal class UiColorWidget : IDataWindowWidget ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); - using (ImRaii.PushId($"row{id}_dark")) - { - if (this.DrawColorColumn(row.Dark) && adjacentRow.HasValue) - DrawEdgePreview(id, row.Dark, adjacentRow.Value.Dark); - } + ImGui.PushID($"row{id}_dark"); + if (this.DrawColorColumn(row.Dark) && + adjacentRow.HasValue) + DrawEdgePreview(id, row.Dark, adjacentRow.Value.Dark); + ImGui.PopID(); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); - using (ImRaii.PushId($"row{id}_light")) - { - if (this.DrawColorColumn(row.Light) && adjacentRow.HasValue) - DrawEdgePreview(id, row.Light, adjacentRow.Value.Light); - } + ImGui.PushID($"row{id}_light"); + if (this.DrawColorColumn(row.Light) && + adjacentRow.HasValue) + DrawEdgePreview(id, row.Light, adjacentRow.Value.Light); + ImGui.PopID(); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); - using (ImRaii.PushId($"row{id}_classic")) - { - if (this.DrawColorColumn(row.ClassicFF) && adjacentRow.HasValue) - DrawEdgePreview(id, row.ClassicFF, adjacentRow.Value.ClassicFF); - } + ImGui.PushID($"row{id}_classic"); + if (this.DrawColorColumn(row.ClassicFF) && + adjacentRow.HasValue) + DrawEdgePreview(id, row.ClassicFF, adjacentRow.Value.ClassicFF); + ImGui.PopID(); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); - using (ImRaii.PushId($"row{id}_blue")) - { - if (this.DrawColorColumn(row.ClearBlue) && adjacentRow.HasValue) - DrawEdgePreview(id, row.ClearBlue, adjacentRow.Value.ClearBlue); - } - - ImGui.TableNextColumn(); - ImGui.AlignTextToFramePadding(); - using (ImRaii.PushId($"row{id}_white")) - { - if (this.DrawColorColumn(row.ClearWhite) && adjacentRow.HasValue) - DrawEdgePreview(id, row.ClearWhite, adjacentRow.Value.ClearWhite); - } - - ImGui.TableNextColumn(); - ImGui.AlignTextToFramePadding(); - using (ImRaii.PushId($"row{id}_green")) - { - if (this.DrawColorColumn(row.ClearGreen) && adjacentRow.HasValue) - DrawEdgePreview(id, row.ClearGreen, adjacentRow.Value.ClearGreen); - } + ImGui.PushID($"row{id}_blue"); + if (this.DrawColorColumn(row.ClearBlue) && + adjacentRow.HasValue) + DrawEdgePreview(id, row.ClearBlue, adjacentRow.Value.ClearBlue); + ImGui.PopID(); } } clipper.Destroy(); + ImGui.EndTable(); } private static void DrawEdgePreview(uint id, uint sheetColor, uint sheetColor2) { - using var tooltip = ImRaii.Tooltip(); - + ImGui.BeginTooltip(); Span<byte> buf = stackalloc byte[256]; var ptr = 0; ptr += Encoding.UTF8.GetBytes("<colortype(", buf[ptr..]); @@ -188,6 +166,7 @@ internal class UiColorWidget : IDataWindowWidget EdgeColor = BinaryPrimitives.ReverseEndianness(sheetColor) | 0xFF000000u, WrapWidth = float.PositiveInfinity, }); + ImGui.EndTooltip(); } private bool DrawColorColumn(uint sheetColor) diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/UldWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/UldWidget.cs index f94ea0729..bc12f4d28 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/UldWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/UldWidget.cs @@ -12,9 +12,7 @@ using Dalamud.Interface.Colors; using Dalamud.Interface.Components; using Dalamud.Interface.Textures.Internal; using Dalamud.Interface.Utility; -using Dalamud.Interface.Utility.Raii; using Dalamud.Memory; - using Lumina.Data.Files; using Lumina.Data.Parsing.Uld; @@ -27,10 +25,9 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// </summary> internal class UldWidget : IDataWindowWidget { - private const string UldBaseBath = "ui/uld/"; - // ULD styles can be hardcoded for now as they don't add new ones regularly. Can later try and find where to load these from in the game EXE. - private static readonly string[] ThemeDisplayNames = ["Dark", "Light", "Classic FF", "Clear Blue", "Clear White", "Clear Green"]; + private static readonly string[] ThemeDisplayNames = ["Dark", "Light", "Classic FF", "Clear Blue"]; + private static readonly string[] ThemeBasePaths = ["ui/uld/", "ui/uld/img01/", "ui/uld/img02/", "ui/uld/img03/"]; // 48 8D 15 ?? ?? ?? ?? is the part of the signatures that contain the string location offset // 48 = 64 bit register prefix @@ -49,7 +46,6 @@ internal class UldWidget : IDataWindowWidget ("48 8D 15 ?? ?? ?? ?? 45 33 C0 E9 ?? ?? ?? ??", 3) ]; - private DataManager dataManager; private CancellationTokenSource? cts; private Task<string[]>? uldNamesTask; @@ -72,8 +68,6 @@ internal class UldWidget : IDataWindowWidget /// <inheritdoc/> public void Load() { - this.dataManager ??= Service<DataManager>.Get(); - this.cts?.Cancel(); ClearTask(ref this.uldNamesTask); this.uldNamesTask = null; @@ -164,19 +158,17 @@ internal class UldWidget : IDataWindowWidget ImGuiColors.DalamudRed, $"Error: {nameof(UldFile.AssetData)} is not populated."); } - else + else if (ImGui.BeginTable("##uldTextureEntries"u8, 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders)) { - using var table = ImRaii.Table("##uldTextureEntries"u8, 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders); - if (table.Success) - { - ImGui.TableSetupColumn("Id"u8, ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("000000"u8).X); - ImGui.TableSetupColumn("Path"u8, ImGuiTableColumnFlags.WidthStretch); - ImGui.TableSetupColumn("Actions"u8, ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Preview___"u8).X); - ImGui.TableHeadersRow(); + ImGui.TableSetupColumn("Id"u8, ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("000000"u8).X); + ImGui.TableSetupColumn("Path"u8, ImGuiTableColumnFlags.WidthStretch); + ImGui.TableSetupColumn("Actions"u8, ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Preview___"u8).X); + ImGui.TableHeadersRow(); - foreach (var textureEntry in uld.AssetData) - this.DrawTextureEntry(textureEntry, textureManager); - } + foreach (var textureEntry in uld.AssetData) + this.DrawTextureEntry(textureEntry, textureManager); + + ImGui.EndTable(); } } @@ -271,7 +263,7 @@ internal class UldWidget : IDataWindowWidget } private string ToThemedPath(string path) => - UldBaseBath + (this.selectedTheme > 0 ? $"img{this.selectedTheme:D2}/" : string.Empty) + path[UldBaseBath.Length..]; + ThemeBasePaths[this.selectedTheme] + path[ThemeBasePaths[0].Length..]; private void DrawTextureEntry(UldRoot.TextureEntry textureEntry, TextureManager textureManager) { @@ -290,7 +282,7 @@ internal class UldWidget : IDataWindowWidget if (ImGui.IsItemHovered()) { - using var tooltip = ImRaii.Tooltip(); + ImGui.BeginTooltip(); var texturePath = GetStringNullTerminated(textureEntry.Path); ImGui.Text($"Base path at {texturePath}:"); @@ -299,18 +291,17 @@ internal class UldWidget : IDataWindowWidget else if (e is not null) ImGui.Text(e.ToString()); - if (this.selectedTheme != 0 && (textureEntry.ThemeSupportBitmask & (1 << (this.selectedTheme - 1))) != 0) + if (this.selectedTheme != 0) { var texturePathThemed = this.ToThemedPath(texturePath); - if (this.dataManager.FileExists(texturePathThemed)) - { - ImGui.Text($"Themed path at {texturePathThemed}:"); - if (textureManager.Shared.GetFromGame(texturePathThemed).TryGetWrap(out wrap, out e)) - ImGui.Image(wrap.Handle, wrap.Size); - else if (e is not null) - ImGui.Text(e.ToString()); - } + ImGui.Text($"Themed path at {texturePathThemed}:"); + if (textureManager.Shared.GetFromGame(texturePathThemed).TryGetWrap(out wrap, out e)) + ImGui.Image(wrap.Handle, wrap.Size); + else if (e is not null) + ImGui.Text(e.ToString()); } + + ImGui.EndTooltip(); } } @@ -319,14 +310,15 @@ internal class UldWidget : IDataWindowWidget ImGui.SliderInt("FrameData"u8, ref this.selectedFrameData, 0, timeline.FrameData.Length - 1); var frameData = timeline.FrameData[this.selectedFrameData]; ImGui.Text($"FrameInfo: {frameData.StartFrame} -> {frameData.EndFrame}"); - - using var indent = ImRaii.PushIndent(); + ImGui.Indent(); foreach (var frameDataKeyGroup in frameData.KeyGroups) { ImGui.Text($"{frameDataKeyGroup.Usage:G} {frameDataKeyGroup.Type:G}"); foreach (var keyframe in frameDataKeyGroup.Frames) this.DrawTimelineKeyGroupFrame(keyframe); } + + ImGui.Unindent(); } private void DrawTimelineKeyGroupFrame(IKeyframe frame) @@ -334,7 +326,8 @@ internal class UldWidget : IDataWindowWidget switch (frame) { case BaseKeyframeData baseKeyframeData: - ImGui.Text($"Time: {baseKeyframeData.Time} | Interpolation: {baseKeyframeData.Interpolation} | Acceleration: {baseKeyframeData.Acceleration} | Deceleration: {baseKeyframeData.Deceleration}"); + ImGui.Text( + $"Time: {baseKeyframeData.Time} | Interpolation: {baseKeyframeData.Interpolation} | Acceleration: {baseKeyframeData.Acceleration} | Deceleration: {baseKeyframeData.Deceleration}"); break; case Float1Keyframe float1Keyframe: this.DrawTimelineKeyGroupFrame(float1Keyframe.Keyframe); @@ -349,7 +342,8 @@ internal class UldWidget : IDataWindowWidget case Float3Keyframe float3Keyframe: this.DrawTimelineKeyGroupFrame(float3Keyframe.Keyframe); ImGui.SameLine(0, 0); - ImGui.Text($" | Value1: {float3Keyframe.Value[0]} | Value2: {float3Keyframe.Value[1]} | Value3: {float3Keyframe.Value[2]}"); + ImGui.Text( + $" | Value1: {float3Keyframe.Value[0]} | Value2: {float3Keyframe.Value[1]} | Value3: {float3Keyframe.Value[2]}"); break; case SByte1Keyframe sbyte1Keyframe: this.DrawTimelineKeyGroupFrame(sbyte1Keyframe.Keyframe); @@ -364,7 +358,8 @@ internal class UldWidget : IDataWindowWidget case SByte3Keyframe sbyte3Keyframe: this.DrawTimelineKeyGroupFrame(sbyte3Keyframe.Keyframe); ImGui.SameLine(0, 0); - ImGui.Text($" | Value1: {sbyte3Keyframe.Value[0]} | Value2: {sbyte3Keyframe.Value[1]} | Value3: {sbyte3Keyframe.Value[2]}"); + ImGui.Text( + $" | Value1: {sbyte3Keyframe.Value[0]} | Value2: {sbyte3Keyframe.Value[1]} | Value3: {sbyte3Keyframe.Value[2]}"); break; case Byte1Keyframe byte1Keyframe: this.DrawTimelineKeyGroupFrame(byte1Keyframe.Keyframe); @@ -379,7 +374,8 @@ internal class UldWidget : IDataWindowWidget case Byte3Keyframe byte3Keyframe: this.DrawTimelineKeyGroupFrame(byte3Keyframe.Keyframe); ImGui.SameLine(0, 0); - ImGui.Text($" | Value1: {byte3Keyframe.Value[0]} | Value2: {byte3Keyframe.Value[1]} | Value3: {byte3Keyframe.Value[2]}"); + ImGui.Text( + $" | Value1: {byte3Keyframe.Value[0]} | Value2: {byte3Keyframe.Value[1]} | Value3: {byte3Keyframe.Value[2]}"); break; case Short1Keyframe short1Keyframe: this.DrawTimelineKeyGroupFrame(short1Keyframe.Keyframe); @@ -394,7 +390,8 @@ internal class UldWidget : IDataWindowWidget case Short3Keyframe short3Keyframe: this.DrawTimelineKeyGroupFrame(short3Keyframe.Keyframe); ImGui.SameLine(0, 0); - ImGui.Text($" | Value1: {short3Keyframe.Value[0]} | Value2: {short3Keyframe.Value[1]} | Value3: {short3Keyframe.Value[2]}"); + ImGui.Text( + $" | Value1: {short3Keyframe.Value[0]} | Value2: {short3Keyframe.Value[1]} | Value3: {short3Keyframe.Value[2]}"); break; case UShort1Keyframe ushort1Keyframe: this.DrawTimelineKeyGroupFrame(ushort1Keyframe.Keyframe); @@ -409,7 +406,8 @@ internal class UldWidget : IDataWindowWidget case UShort3Keyframe ushort3Keyframe: this.DrawTimelineKeyGroupFrame(ushort3Keyframe.Keyframe); ImGui.SameLine(0, 0); - ImGui.Text($" | Value1: {ushort3Keyframe.Value[0]} | Value2: {ushort3Keyframe.Value[1]} | Value3: {ushort3Keyframe.Value[2]}"); + ImGui.Text( + $" | Value1: {ushort3Keyframe.Value[0]} | Value2: {ushort3Keyframe.Value[1]} | Value3: {ushort3Keyframe.Value[2]}"); break; case Int1Keyframe int1Keyframe: this.DrawTimelineKeyGroupFrame(int1Keyframe.Keyframe); @@ -424,7 +422,8 @@ internal class UldWidget : IDataWindowWidget case Int3Keyframe int3Keyframe: this.DrawTimelineKeyGroupFrame(int3Keyframe.Keyframe); ImGui.SameLine(0, 0); - ImGui.Text($" | Value1: {int3Keyframe.Value[0]} | Value2: {int3Keyframe.Value[1]} | Value3: {int3Keyframe.Value[2]}"); + ImGui.Text( + $" | Value1: {int3Keyframe.Value[0]} | Value2: {int3Keyframe.Value[1]} | Value3: {int3Keyframe.Value[2]}"); break; case UInt1Keyframe uint1Keyframe: this.DrawTimelineKeyGroupFrame(uint1Keyframe.Keyframe); @@ -439,7 +438,8 @@ internal class UldWidget : IDataWindowWidget case UInt3Keyframe uint3Keyframe: this.DrawTimelineKeyGroupFrame(uint3Keyframe.Keyframe); ImGui.SameLine(0, 0); - ImGui.Text($" | Value1: {uint3Keyframe.Value[0]} | Value2: {uint3Keyframe.Value[1]} | Value3: {uint3Keyframe.Value[2]}"); + ImGui.Text( + $" | Value1: {uint3Keyframe.Value[0]} | Value2: {uint3Keyframe.Value[1]} | Value3: {uint3Keyframe.Value[2]}"); break; case Bool1Keyframe bool1Keyframe: this.DrawTimelineKeyGroupFrame(bool1Keyframe.Keyframe); @@ -454,22 +454,28 @@ internal class UldWidget : IDataWindowWidget case Bool3Keyframe bool3Keyframe: this.DrawTimelineKeyGroupFrame(bool3Keyframe.Keyframe); ImGui.SameLine(0, 0); - ImGui.Text($" | Value1: {bool3Keyframe.Value[0]} | Value2: {bool3Keyframe.Value[1]} | Value3: {bool3Keyframe.Value[2]}"); + ImGui.Text( + $" | Value1: {bool3Keyframe.Value[0]} | Value2: {bool3Keyframe.Value[1]} | Value3: {bool3Keyframe.Value[2]}"); break; case ColorKeyframe colorKeyframe: this.DrawTimelineKeyGroupFrame(colorKeyframe.Keyframe); ImGui.SameLine(0, 0); - ImGui.Text($" | Add: {colorKeyframe.AddRed} {colorKeyframe.AddGreen} {colorKeyframe.AddBlue} | Multiply: {colorKeyframe.MultiplyRed} {colorKeyframe.MultiplyGreen} {colorKeyframe.MultiplyBlue}"); + ImGui.Text( + $" | Add: {colorKeyframe.AddRed} {colorKeyframe.AddGreen} {colorKeyframe.AddBlue} | Multiply: {colorKeyframe.MultiplyRed} {colorKeyframe.MultiplyGreen} {colorKeyframe.MultiplyBlue}"); break; case LabelKeyframe labelKeyframe: this.DrawTimelineKeyGroupFrame(labelKeyframe.Keyframe); ImGui.SameLine(0, 0); - ImGui.Text($" | LabelCommand: {labelKeyframe.LabelCommand} | JumpId: {labelKeyframe.JumpId} | LabelId: {labelKeyframe.LabelId}"); + ImGui.Text( + $" | LabelCommand: {labelKeyframe.LabelCommand} | JumpId: {labelKeyframe.JumpId} | LabelId: {labelKeyframe.LabelId}"); break; } } - private void DrawParts(UldRoot.PartsData partsData, UldRoot.TextureEntry[] textureEntries, TextureManager textureManager) + private void DrawParts( + UldRoot.PartsData partsData, + UldRoot.TextureEntry[] textureEntries, + TextureManager textureManager) { for (var index = 0; index < partsData.Parts.Length; index++) { @@ -535,9 +541,10 @@ internal class UldWidget : IDataWindowWidget if (ImGui.IsItemHovered()) { - using var tooltip = ImRaii.Tooltip(); + ImGui.BeginTooltip(); ImGui.Text("Click to copy:"u8); ImGui.Text(texturePath); + ImGui.EndTooltip(); } } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/VfsWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/VfsWidget.cs index d01bd7d78..f044b2989 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/VfsWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/VfsWidget.cs @@ -4,7 +4,6 @@ using System.IO; using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Storage; - using Serilog; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -18,7 +17,7 @@ internal class VfsWidget : IDataWindowWidget private int reps = 1; /// <inheritdoc/> - public string[]? CommandShortcuts { get; init; } = ["vfs"]; + public string[]? CommandShortcuts { get; init; } = { "vfs" }; /// <inheritdoc/> public string DisplayName { get; init; } = "VFS Performance"; diff --git a/Dalamud/Interface/Internal/Windows/GamepadModeNotifierWindow.cs b/Dalamud/Interface/Internal/Windows/GamepadModeNotifierWindow.cs index edad04951..91f7f02d9 100644 --- a/Dalamud/Interface/Internal/Windows/GamepadModeNotifierWindow.cs +++ b/Dalamud/Interface/Internal/Windows/GamepadModeNotifierWindow.cs @@ -1,7 +1,6 @@ using System.Numerics; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; diff --git a/Dalamud/Interface/Internal/Windows/PluginImageCache.cs b/Dalamud/Interface/Internal/Windows/PluginImageCache.cs index fb77cf1cf..e95d2e1b8 100644 --- a/Dalamud/Interface/Internal/Windows/PluginImageCache.cs +++ b/Dalamud/Interface/Internal/Windows/PluginImageCache.cs @@ -6,6 +6,7 @@ using System.Net; using System.Threading; using System.Threading.Tasks; +using Dalamud.Game; using Dalamud.Interface.Textures.Internal; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Networking.Http; @@ -14,7 +15,6 @@ using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Internal.Types.Manifest; using Dalamud.Storage.Assets; using Dalamud.Utility; - using Serilog; namespace Dalamud.Interface.Internal.Windows; @@ -51,8 +51,8 @@ internal class PluginImageCache : IInternalDisposableService [ServiceManager.ServiceDependency] private readonly HappyHttpClient happyHttpClient = Service<HappyHttpClient>.Get(); - private readonly BlockingCollection<Tuple<ulong, Func<Task>>> downloadQueue = []; - private readonly BlockingCollection<Func<Task>> loadQueue = []; + private readonly BlockingCollection<Tuple<ulong, Func<Task>>> downloadQueue = new(); + private readonly BlockingCollection<Func<Task>> loadQueue = new(); private readonly CancellationTokenSource cancelToken = new(); private readonly Task downloadTask; private readonly Task loadTask; @@ -144,7 +144,7 @@ internal class PluginImageCache : IInternalDisposableService this.downloadQueue.CompleteAdding(); this.loadQueue.CompleteAdding(); - if (!Task.WaitAll([this.loadTask, this.downloadTask], 4000)) + if (!Task.WaitAll(new[] { this.loadTask, this.downloadTask }, 4000)) { Log.Error("Plugin Image download/load thread has not cancelled in time"); } @@ -357,7 +357,7 @@ internal class PluginImageCache : IInternalDisposableService try { token.ThrowIfCancellationRequested(); - if (pendingFuncs.Count == 0) + if (!pendingFuncs.Any()) { if (!this.downloadQueue.TryTake(out var taskTuple, -1, token)) return; @@ -373,7 +373,7 @@ internal class PluginImageCache : IInternalDisposableService pendingFuncs = pendingFuncs.OrderBy(x => x.Item1).ToList(); var item1 = pendingFuncs.Last().Item1; - while (pendingFuncs.Count != 0 && pendingFuncs.Last().Item1 == item1) + while (pendingFuncs.Any() && pendingFuncs.Last().Item1 == item1) { token.ThrowIfCancellationRequested(); while (runningTasks.Count >= concurrency) diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/DalamudChangelogManager.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/DalamudChangelogManager.cs index bbc92efb5..f6171e192 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/DalamudChangelogManager.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/DalamudChangelogManager.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Net.Http.Json; using System.Threading.Tasks; @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Dalamud.Networking.Http; using Dalamud.Plugin.Internal; using Dalamud.Utility; - using Serilog; namespace Dalamud.Interface.Internal.Windows.PluginInstaller; diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginChangelogEntry.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginChangelogEntry.cs index 3f964b4b8..879034fd4 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginChangelogEntry.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginChangelogEntry.cs @@ -1,7 +1,5 @@ -using CheapLoc; - +using CheapLoc; using Dalamud.Plugin.Internal.Types; - using Serilog; namespace Dalamud.Interface.Internal.Windows.PluginInstaller; diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index d132f0d8d..b203b3894 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -10,7 +10,6 @@ using System.Threading; using System.Threading.Tasks; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Console; @@ -42,7 +41,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller; /// </summary> internal class PluginInstallerWindow : Window, IDisposable { - private static readonly ModuleLog Log = ModuleLog.Create<PluginInstallerWindow>(); + private static readonly ModuleLog Log = new("PLUGINW"); private readonly Vector4 changelogBgColor = new(0.114f, 0.584f, 0.192f, 0.678f); private readonly Vector4 changelogTextColor = new(0.812f, 1.000f, 0.816f, 1.000f); @@ -50,7 +49,7 @@ internal class PluginInstallerWindow : Window, IDisposable private readonly PluginImageCache imageCache; private readonly PluginCategoryManager categoryManager = new(); - private readonly List<int> openPluginCollapsibles = []; + private readonly List<int> openPluginCollapsibles = new(); private readonly DateTime timeLoaded; @@ -114,9 +113,9 @@ internal class PluginInstallerWindow : Window, IDisposable private List<PluginUpdateStatus>? updatedPlugins; [SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "Makes sense like this")] - private List<RemotePluginManifest> pluginListAvailable = []; - private List<LocalPlugin> pluginListInstalled = []; - private List<AvailablePluginUpdate> pluginListUpdatable = []; + private List<RemotePluginManifest> pluginListAvailable = new(); + private List<LocalPlugin> pluginListInstalled = new(); + private List<AvailablePluginUpdate> pluginListUpdatable = new(); private bool hasDevPlugins = false; private bool hasHiddenPlugins = false; @@ -303,7 +302,8 @@ internal class PluginInstallerWindow : Window, IDisposable this.profileManagerWidget.Reset(); - if (this.staleDalamudNewVersion == null && !Versioning.GetActiveTrack().IsNullOrEmpty()) + var config = Service<DalamudConfiguration>.Get(); + if (this.staleDalamudNewVersion == null && !config.DalamudBetaKind.IsNullOrEmpty()) { Service<DalamudReleases>.Get().GetVersionForCurrentTrack().ContinueWith(t => { @@ -311,10 +311,10 @@ internal class PluginInstallerWindow : Window, IDisposable return; var versionInfo = t.Result; - if (versionInfo.AssemblyVersion != Versioning.GetScmVersion()) - { + if (versionInfo.AssemblyVersion != Util.GetScmVersion() && + versionInfo.Track != "release" && + string.Equals(versionInfo.Key, config.DalamudBetaKey, StringComparison.OrdinalIgnoreCase)) this.staleDalamudNewVersion = versionInfo.AssemblyVersion; - } }); } } @@ -1671,7 +1671,7 @@ internal class PluginInstallerWindow : Window, IDisposable DrawWarningIcon(); DrawLinesCentered("A new version of Dalamud is available.\n" + "Please restart the game to ensure compatibility with updated plugins.\n" + - $"old: {Versioning.GetScmVersion()} new: {this.staleDalamudNewVersion}"); + $"old: {Util.GetScmVersion()} new: {this.staleDalamudNewVersion}"); ImGuiHelpers.ScaledDummy(10); } @@ -2361,7 +2361,7 @@ internal class PluginInstallerWindow : Window, IDisposable else if (!string.IsNullOrWhiteSpace(manifest.Description)) { const int punchlineLen = 200; - var firstLine = manifest.Description.Split(['\r', '\n'])[0]; + var firstLine = manifest.Description.Split(new[] { '\r', '\n' })[0]; ImGui.TextWrapped(firstLine.Length < punchlineLen ? firstLine @@ -2462,7 +2462,7 @@ internal class PluginInstallerWindow : Window, IDisposable var isOutdated = effectiveApiLevel < PluginManager.DalamudApiLevel; var isIncompatible = manifest.MinimumDalamudVersion != null && - manifest.MinimumDalamudVersion > Versioning.GetAssemblyVersionParsed(); + manifest.MinimumDalamudVersion > Util.AssemblyVersionParsed; var enableInstallButton = this.updateStatus != OperationStatus.InProgress && this.installStatus != OperationStatus.InProgress && @@ -3804,7 +3804,7 @@ internal class PluginInstallerWindow : Window, IDisposable if (!manifest.Punchline.IsNullOrEmpty()) scores.Add(matcher.Matches(manifest.Punchline.ToLowerInvariant()) * 100); if (manifest.Tags != null) - scores.Add(matcher.MatchesAny(manifest.Tags.Select(tag => tag.ToLowerInvariant()).ToArray()) * 100); + scores.Add(matcher.MatchesAny(manifest.Tags.ToArray()) * 100); return scores.Max(); } diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs index c01d7f390..fb2719868 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/ProfileManagerWidget.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Threading.Tasks; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; @@ -16,7 +15,6 @@ using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin.Internal; using Dalamud.Plugin.Internal.Profiles; using Dalamud.Utility; - using Serilog; namespace Dalamud.Interface.Internal.Windows.PluginInstaller; @@ -405,7 +403,7 @@ internal class ProfileManagerWidget ImGui.Text(Locs.StartupBehavior); if (ImGui.BeginCombo("##startupBehaviorPicker"u8, Locs.PolicyToLocalisedName(profile.StartupPolicy))) { - foreach (var policy in Enum.GetValues<ProfileModelV1.ProfileStartupPolicy>()) + foreach (var policy in Enum.GetValues(typeof(ProfileModelV1.ProfileStartupPolicy)).Cast<ProfileModelV1.ProfileStartupPolicy>()) { var name = Locs.PolicyToLocalisedName(policy); if (ImGui.Selectable(name, profile.StartupPolicy == policy)) diff --git a/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs b/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs index 8b702123c..b8e10020e 100644 --- a/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Reflection; @@ -13,7 +14,6 @@ using Dalamud.Interface.Windowing; using Dalamud.Plugin.Internal; using Dalamud.Plugin.Internal.Types; using Dalamud.Utility; - using Serilog; namespace Dalamud.Interface.Internal.Windows; @@ -186,7 +186,7 @@ internal class PluginStatWindow : Window ImGui.SameLine(); ImGuiComponents.TextWithLabel("Total Average", $"{totalAverage:F4}ms", "All average update times added together"); ImGui.SameLine(); - ImGuiComponents.TextWithLabel("Collective Average", $"{(statsHistory.Length != 0 ? totalAverage / statsHistory.Length : 0):F4}ms", "Average of all average update times"); + ImGuiComponents.TextWithLabel("Collective Average", $"{(statsHistory.Any() ? totalAverage / statsHistory.Length : 0):F4}ms", "Average of all average update times"); ImGui.InputTextWithHint( "###PluginStatWindow_FrameworkSearch"u8, @@ -230,7 +230,7 @@ internal class PluginStatWindow : Window foreach (var handlerHistory in statsHistory) { - if (handlerHistory.Value.Count == 0) + if (!handlerHistory.Value.Any()) { continue; } diff --git a/Dalamud/Interface/Internal/Windows/ProfilerWindow.cs b/Dalamud/Interface/Internal/Windows/ProfilerWindow.cs index 8ff407cd7..abe8d1584 100644 --- a/Dalamud/Interface/Internal/Windows/ProfilerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/ProfilerWindow.cs @@ -19,7 +19,7 @@ public class ProfilerWindow : Window { private double min; private double max; - private List<List<Tuple<double, double>>> occupied = []; + private List<List<Tuple<double, double>>> occupied = new(); /// <summary> /// Initializes a new instance of the <see cref="ProfilerWindow"/> class. @@ -109,7 +109,7 @@ public class ProfilerWindow : Window } if (depth == this.occupied.Count) - this.occupied.Add([]); + this.occupied.Add(new()); this.occupied[depth].Add(Tuple.Create(timingHandle.StartTime, timingHandle.EndTime)); parentDepthDict[timingHandle.Id] = depth; @@ -188,7 +188,7 @@ public class ProfilerWindow : Window } } - var eventTextDepth = maxRectDept + 2; + uint eventTextDepth = maxRectDept + 2; var eventsXPos = new List<float>(); const float eventsXPosFudge = 5f; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs b/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs index 0335cafc5..ea8cd0070 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs @@ -10,7 +10,6 @@ using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; using Dalamud.Logging.Internal; - using Dalamud.Plugin.SelfTest; using Dalamud.Plugin.SelfTest.Internal; using Dalamud.Utility; @@ -22,11 +21,11 @@ namespace Dalamud.Interface.Internal.Windows.SelfTest; /// </summary> internal class SelfTestWindow : Window { - private static readonly ModuleLog Log = ModuleLog.Create<SelfTestWindow>(); + private static readonly ModuleLog Log = new("AGING"); private readonly SelfTestRegistry selfTestRegistry; - private List<SelfTestWithResults> visibleSteps = []; + private List<SelfTestWithResults> visibleSteps = new(); private bool selfTestRunning = false; private SelfTestGroup? currentTestGroup = null; @@ -139,7 +138,7 @@ internal class SelfTestWindow : Window ImGui.SameLine(); var stepNumber = this.currentStep != null ? this.visibleSteps.IndexOf(this.currentStep) : 0; - ImGui.Text($"Step: {stepNumber} / {this.visibleSteps.Count}"); + ImGui.Text($"Step: {stepNumber} / {this.visibleSteps.Count}"); ImGui.Spacing(); diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AddonLifecycleSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AddonLifecycleSelfTestStep.cs index e2c1a40df..d9c9facc7 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AddonLifecycleSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AddonLifecycleSelfTestStep.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Bindings.ImGui; using Dalamud.Game.Addon.Lifecycle; @@ -23,15 +23,15 @@ internal class AddonLifecycleSelfTestStep : ISelfTestStep /// </summary> public AddonLifecycleSelfTestStep() { - this.listeners = - [ + this.listeners = new List<AddonLifecycleEventListener> + { new(AddonEvent.PostSetup, "Character", this.PostSetup), new(AddonEvent.PostUpdate, "Character", this.PostUpdate), new(AddonEvent.PostDraw, "Character", this.PostDraw), new(AddonEvent.PostRefresh, "Character", this.PostRefresh), new(AddonEvent.PostRequestedUpdate, "Character", this.PostRequestedUpdate), new(AddonEvent.PreFinalize, "Character", this.PreFinalize), - ]; + }; } private enum TestStep diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ChatSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ChatSelfTestStep.cs index 851957b4b..7a2631fbf 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ChatSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ChatSelfTestStep.cs @@ -1,5 +1,4 @@ using Dalamud.Bindings.ImGui; -using Dalamud.Game.Chat; using Dalamud.Game.Gui; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; @@ -13,12 +12,8 @@ namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; internal class ChatSelfTestStep : ISelfTestStep { private int step = 0; - private bool subscribedChatMessage = false; - private bool subscribedLogMessage = false; - private bool hasSeenEchoMessage = false; - private bool hasSeenActionMessage = false; - private string actionName = string.Empty; - private string actionUser = string.Empty; + private bool subscribed = false; + private bool hasPassed = false; /// <inheritdoc/> public string Name => "Test Chat"; @@ -39,55 +34,20 @@ internal class ChatSelfTestStep : ISelfTestStep case 1: ImGui.Text("Type \"/e DALAMUD\" in chat..."); - if (!this.subscribedChatMessage) + if (!this.subscribed) { - this.subscribedChatMessage = true; + this.subscribed = true; chatGui.ChatMessage += this.ChatOnOnChatMessage; } - if (this.hasSeenEchoMessage) + if (this.hasPassed) { chatGui.ChatMessage -= this.ChatOnOnChatMessage; - this.subscribedChatMessage = false; - this.step++; + this.subscribed = false; + return SelfTestStepResult.Pass; } break; - - case 2: - ImGui.Text("Use any action (for example Sprint) or be near a player using an action."); - - if (!this.subscribedLogMessage) - { - this.subscribedLogMessage = true; - chatGui.LogMessage += this.ChatOnLogMessage; - } - - if (this.hasSeenActionMessage) - { - ImGui.Text($"{this.actionUser} used {this.actionName}."); - ImGui.Text("Is this correct?"); - - if (ImGui.Button("Yes")) - { - chatGui.LogMessage -= this.ChatOnLogMessage; - this.subscribedLogMessage = false; - this.step++; - } - - ImGui.SameLine(); - if (ImGui.Button("No")) - { - chatGui.LogMessage -= this.ChatOnLogMessage; - this.subscribedLogMessage = false; - return SelfTestStepResult.Fail; - } - } - - break; - - default: - return SelfTestStepResult.Pass; } return SelfTestStepResult.Waiting; @@ -99,9 +59,7 @@ internal class ChatSelfTestStep : ISelfTestStep var chatGui = Service<ChatGui>.Get(); chatGui.ChatMessage -= this.ChatOnOnChatMessage; - chatGui.LogMessage -= this.ChatOnLogMessage; - this.subscribedChatMessage = false; - this.subscribedLogMessage = false; + this.subscribed = false; } private void ChatOnOnChatMessage( @@ -109,17 +67,7 @@ internal class ChatSelfTestStep : ISelfTestStep { if (type == XivChatType.Echo && message.TextValue == "DALAMUD") { - this.hasSeenEchoMessage = true; - } - } - - private void ChatOnLogMessage(ILogMessage message) - { - if (message.LogMessageId == 533 && message.TryGetStringParameter(0, out var value)) - { - this.hasSeenActionMessage = true; - this.actionUser = message.SourceEntity?.Name.ExtractText() ?? "<incorrect>"; - this.actionName = value.ExtractText(); + this.hasPassed = true; } } } diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/CompletionSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/CompletionSelfTestStep.cs index 1f33e5dd2..a34b058bd 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/CompletionSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/CompletionSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Command; +using Dalamud.Interface.Utility; using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ConditionSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ConditionSelfTestStep.cs index 1c9b589d0..89083da48 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ConditionSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ConditionSelfTestStep.cs @@ -1,7 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Conditions; using Dalamud.Plugin.SelfTest; - using Serilog; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ContextMenuSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ContextMenuSelfTestStep.cs index b61c62589..0fe5b4443 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ContextMenuSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ContextMenuSelfTestStep.cs @@ -9,10 +9,8 @@ using Dalamud.Game.Gui.ContextMenu; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Plugin.SelfTest; - using Lumina.Excel; using Lumina.Excel.Sheets; - using Serilog; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FrameworkTaskSchedulerSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FrameworkTaskSchedulerSelfTestStep.cs index c5f3ab76b..eb6909fa7 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FrameworkTaskSchedulerSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FrameworkTaskSchedulerSelfTestStep.cs @@ -4,6 +4,8 @@ using Dalamud.Game; using Dalamud.Plugin.SelfTest; using Dalamud.Utility; +using Microsoft.VisualBasic.Logging; + using Log = Serilog.Log; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GamepadStateSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GamepadStateSelfTestStep.cs index e2ee676df..d272032e7 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GamepadStateSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GamepadStateSelfTestStep.cs @@ -3,10 +3,10 @@ using System.Linq; using Dalamud.Game.ClientState.GamePad; using Dalamud.Interface.Utility; using Dalamud.Plugin.SelfTest; -using Dalamud.Utility; - using Lumina.Text.Payloads; +using LSeStringBuilder = Lumina.Text.SeStringBuilder; + namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; /// <summary> @@ -29,25 +29,25 @@ internal class GamepadStateSelfTestStep : ISelfTestStep (GamepadButtons.L1, 12), }; - using var rssb = new RentedSeStringBuilder(); + var builder = LSeStringBuilder.SharedPool.Get(); - rssb.Builder.Append("Hold down "); + builder.Append("Hold down "); for (var i = 0; i < buttons.Length; i++) { var (button, iconId) = buttons[i]; - rssb.Builder - .BeginMacro(MacroCode.Icon) - .AppendUIntExpression(iconId) - .EndMacro() - .PushColorRgba(gamepadState.Raw(button) == 1 ? 0x0000FF00u : 0x000000FF) - .Append(button.ToString()) - .PopColor() - .Append(i < buttons.Length - 1 ? ", " : "."); + builder.BeginMacro(MacroCode.Icon).AppendUIntExpression(iconId).EndMacro(); + builder.PushColorRgba(gamepadState.Raw(button) == 1 ? 0x0000FF00u : 0x000000FF); + builder.Append(button.ToString()); + builder.PopColor(); + + builder.Append(i < buttons.Length - 1 ? ", " : "."); } - ImGuiHelpers.SeStringWrapped(rssb.Builder.ToReadOnlySeString()); + ImGuiHelpers.SeStringWrapped(builder.ToReadOnlySeString()); + + LSeStringBuilder.SharedPool.Return(builder); if (buttons.All(tuple => gamepadState.Raw(tuple.Button) == 1)) { diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LuminaSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LuminaSelfTestStep.cs index dd8a16689..741dd71b1 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LuminaSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LuminaSelfTestStep.cs @@ -1,7 +1,6 @@ using Dalamud.Data; using Dalamud.Plugin.SelfTest; using Dalamud.Utility; - using Lumina.Excel; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/MarketBoardSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/MarketBoardSelfTestStep.cs index ff6b64383..6a45f343a 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/MarketBoardSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/MarketBoardSelfTestStep.cs @@ -4,6 +4,7 @@ using System.Linq; using Dalamud.Bindings.ImGui; using Dalamud.Game.MarketBoard; using Dalamud.Game.Network.Structures; +using Dalamud.Interface.Utility; using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NamePlateSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NamePlateSelfTestStep.cs index 7136c8801..9cc6045a6 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NamePlateSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NamePlateSelfTestStep.cs @@ -36,7 +36,7 @@ internal class NamePlateSelfTestStep : ISelfTestStep namePlateGui.OnNamePlateUpdate += this.OnNamePlateUpdate; namePlateGui.OnDataUpdate += this.OnDataUpdate; namePlateGui.RequestRedraw(); - this.updateCount = []; + this.updateCount = new Dictionary<ulong, int>(); this.currentSubStep++; break; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NounProcessorSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NounProcessorSelfTestStep.cs index ccccc691c..ccb23d395 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NounProcessorSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NounProcessorSelfTestStep.cs @@ -191,29 +191,6 @@ internal class NounProcessorSelfTestStep : ISelfTestStep new(nameof(LSheets.Item), 44348, ClientLanguage.French, 2, (int)FrenchArticleType.PossessiveFirstPerson, 1, "mes mémoquartz inhabituels fantasmagoriques"), new(nameof(LSheets.Item), 44348, ClientLanguage.French, 2, (int)FrenchArticleType.PossessiveSecondPerson, 1, "tes mémoquartz inhabituels fantasmagoriques"), new(nameof(LSheets.Item), 44348, ClientLanguage.French, 2, (int)FrenchArticleType.PossessiveThirdPerson, 1, "ses mémoquartz inhabituels fantasmagoriques"), - - // ColumnOffset tests - - new(nameof(LSheets.BeastTribe), 1, ClientLanguage.English, 1, (int)EnglishArticleType.Indefinite, 1, "a Amalj'aa"), - new(nameof(LSheets.BeastTribe), 1, ClientLanguage.English, 1, (int)EnglishArticleType.Definite, 1, "the Amalj'aa"), - - new(nameof(LSheets.DeepDungeonEquipment), 1, ClientLanguage.English, 1, (int)EnglishArticleType.Indefinite, 1, "an aetherpool arm"), - new(nameof(LSheets.DeepDungeonEquipment), 1, ClientLanguage.English, 1, (int)EnglishArticleType.Definite, 1, "the aetherpool arm"), - - new(nameof(LSheets.DeepDungeonItem), 1, ClientLanguage.English, 1, (int)EnglishArticleType.Indefinite, 1, "a pomander of safety"), - new(nameof(LSheets.DeepDungeonItem), 1, ClientLanguage.English, 1, (int)EnglishArticleType.Definite, 1, "the pomander of safety"), - - new(nameof(LSheets.DeepDungeonMagicStone), 1, ClientLanguage.English, 1, (int)EnglishArticleType.Indefinite, 1, "a splinter of Inferno magicite"), - new(nameof(LSheets.DeepDungeonMagicStone), 1, ClientLanguage.English, 1, (int)EnglishArticleType.Definite, 1, "the splinter of Inferno magicite"), - - new(nameof(LSheets.DeepDungeonDemiclone), 1, ClientLanguage.English, 1, (int)EnglishArticleType.Indefinite, 1, "an Unei demiclone"), - new(nameof(LSheets.DeepDungeonDemiclone), 1, ClientLanguage.English, 1, (int)EnglishArticleType.Definite, 1, "the Unei demiclone"), - - new(nameof(LSheets.Glasses), 1, ClientLanguage.English, 1, (int)EnglishArticleType.Indefinite, 1, "a pair of oval spectacles"), - new(nameof(LSheets.Glasses), 1, ClientLanguage.English, 1, (int)EnglishArticleType.Definite, 1, "the pair of oval spectacles"), - - new(nameof(LSheets.GlassesStyle), 1, ClientLanguage.English, 1, (int)EnglishArticleType.Indefinite, 1, "a shaded spectacles"), - new(nameof(LSheets.GlassesStyle), 1, ClientLanguage.English, 1, (int)EnglishArticleType.Definite, 1, "the shaded spectacles"), ]; private enum GermanCases diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SheetRedirectResolverSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SheetRedirectResolverSelfTestStep.cs index c99ec91de..c285fda46 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SheetRedirectResolverSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SheetRedirectResolverSelfTestStep.cs @@ -4,7 +4,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game; using Dalamud.Game.Text.Evaluator.Internal; using Dalamud.Plugin.SelfTest; - using FFXIVClientStructs.FFXIV.Client.System.String; using FFXIVClientStructs.FFXIV.Client.UI.Misc; diff --git a/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs b/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs index aa8d1dc3a..581ef3746 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Numerics; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; @@ -47,7 +46,6 @@ internal sealed class SettingsWindow : Window new SettingsTabLook(), new SettingsTabAutoUpdates(), new SettingsTabDtr(), - new SettingsTabBadge(), new SettingsTabExperimental(), new SettingsTabAbout() ]; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAbout.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAbout.cs index 1d5eebb85..74b9b0fd7 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAbout.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAbout.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Numerics; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui; using Dalamud.Interface.GameFonts; @@ -16,7 +15,6 @@ using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin.Internal; using Dalamud.Storage.Assets; using Dalamud.Utility; - using FFXIVClientStructs.FFXIV.Client.Game.UI; namespace Dalamud.Interface.Internal.Windows.Settings.Tabs; @@ -225,7 +223,7 @@ Contribute at: https://github.com/goatcorp/Dalamud .Select(plugin => $"{plugin.Manifest.Name} by {plugin.Manifest.Author}\n") .Aggregate(string.Empty, (current, next) => $"{current}{next}"); - this.creditsText = string.Format(CreditsTextTempl, typeof(Dalamud).Assembly.GetName().Version, pluginCredits, Versioning.GetGitHashClientStructs()); + this.creditsText = string.Format(CreditsTextTempl, typeof(Dalamud).Assembly.GetName().Version, pluginCredits, Util.GetGitHashClientStructs()); var gameGui = Service<GameGui>.Get(); var playerState = PlayerState.Instance(); diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAutoUpdate.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAutoUpdate.cs index b03a0f51c..6b99c5c24 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAutoUpdate.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAutoUpdate.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Numerics; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabBadge.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabBadge.cs deleted file mode 100644 index e39c1952c..000000000 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabBadge.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq; - -using CheapLoc; -using Dalamud.Bindings.ImGui; -using Dalamud.Interface.Colors; -using Dalamud.Interface.Internal.Badge; -using Dalamud.Interface.Internal.Windows.Settings.Widgets; -using Dalamud.Interface.Utility; -using Dalamud.Storage.Assets; -using Dalamud.Utility.Internal; - -namespace Dalamud.Interface.Internal.Windows.Settings.Tabs; - -[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")] -internal sealed class SettingsTabBadge : SettingsTab -{ - private string badgePassword = string.Empty; - private bool badgeWasError = false; - - public override string Title => Loc.Localize("DalamudSettingsBadge", "Badges"); - - public override SettingsOpenKind Kind => SettingsOpenKind.Badge; - - public override SettingsEntry[] Entries { get; } = - [ - new SettingsEntry<bool>( - LazyLoc.Localize("DalamudSettingsShowBadgesOnTitleScreen", "Show Badges on Title Screen"), - LazyLoc.Localize("DalamudSettingsShowBadgesOnTitleScreenHint", "If enabled, your unlocked badges will also be shown on the title screen."), - c => c.ShowBadgesOnTitleScreen, - (v, c) => c.ShowBadgesOnTitleScreen = v), - ]; - - public override void Draw() - { - var badgeManager = Service<BadgeManager>.Get(); - var dalamudInterface = Service<DalamudInterface>.Get(); - - ImGui.TextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingBadgesHint", "On this tab, you can unlock small badges that show on your title screen.\nBadge codes are usually given out during community events or contests.")); - - ImGuiHelpers.ScaledDummy(5); - - ImGui.Text(Loc.Localize("DalamudSettingsBadgesUnlock", "Unlock a badge")); - ImGui.TextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsBadgesUnlockHint", "If you have received a code for a badge, enter it here to unlock the badge.")); - ImGui.InputTextWithHint( - "##BadgePassword", - Loc.Localize("DalamudSettingsBadgesUnlockHintInput", "Enter badge code here"), - ref this.badgePassword, - 100); - ImGui.SameLine(); - if (ImGui.Button(Loc.Localize("DalamudSettingsBadgesUnlockButton", "Unlock Badge"))) - { - if (badgeManager.TryUnlockBadge(this.badgePassword.Trim(), BadgeUnlockMethod.User, out var unlockedBadge)) - { - dalamudInterface.StartBadgeUnlockAnimation(unlockedBadge); - this.badgeWasError = false; - } - else - { - this.badgeWasError = true; - } - } - - if (this.badgeWasError) - { - ImGuiHelpers.ScaledDummy(5); - ImGui.TextColored(ImGuiColors.DalamudRed, Loc.Localize("DalamudSettingsBadgesUnlockError", "Failed to unlock badge. The code may be invalid or you may have already unlocked this badge.")); - } - - ImGuiHelpers.ScaledDummy(5); - - base.Draw(); - - ImGui.Separator(); - - ImGuiHelpers.ScaledDummy(5); - - var haveBadges = badgeManager.UnlockedBadges.ToArray(); - - if (haveBadges.Length == 0) - { - ImGui.TextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsBadgesDidNone", "You did not unlock any badges yet.")); - } - - var badgeTexture = Service<DalamudAssetManager>.Get().GetDalamudTextureWrap(DalamudAsset.BadgeAtlas); - foreach (var badge in haveBadges) - { - var uvs = badge.GetIconUv(badgeTexture.Width, badgeTexture.Height); - var sectionSize = ImGuiHelpers.GlobalScale * 66; - - var startCursor = ImGui.GetCursorPos(); - - ImGui.SetCursorPos(startCursor); - - var iconSize = ImGuiHelpers.ScaledVector2(64, 64); - var cursorBeforeImage = ImGui.GetCursorPos(); - var rectOffset = ImGui.GetWindowContentRegionMin() + ImGui.GetWindowPos(); - - if (ImGui.IsRectVisible(rectOffset + cursorBeforeImage, rectOffset + cursorBeforeImage + iconSize)) - { - ImGui.Image(badgeTexture.Handle, iconSize, uvs.Uv0, uvs.Uv1); - ImGui.SameLine(); - ImGui.SetCursorPos(cursorBeforeImage); - } - - ImGui.SameLine(); - - ImGuiHelpers.ScaledDummy(5); - ImGui.SameLine(); - - var cursor = ImGui.GetCursorPos(); - - // Name - ImGui.Text(badge.Name()); - - cursor.Y += ImGui.GetTextLineHeightWithSpacing(); - ImGui.SetCursorPos(cursor); - - // Description - ImGui.TextWrapped(badge.Description()); - - startCursor.Y += sectionSize; - ImGui.SetCursorPos(startCursor); - - ImGuiHelpers.ScaledDummy(5); - } - } -} diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabDtr.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabDtr.cs index 18382b1f8..4b055b35b 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabDtr.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabDtr.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Numerics; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Game.Gui.Dtr; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs index d8bf31bfd..9b2c418b6 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs @@ -4,7 +4,6 @@ using System.Numerics; using System.Text; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Game; @@ -66,14 +65,7 @@ internal sealed class SettingsTabLook : SettingsTab { try { - if (b) - { - Service<InterfaceManager>.GetNullable()?.SetImmersiveModeFromSystemTheme(); - } - else - { - Service<InterfaceManager>.GetNullable()?.SetImmersiveMode(false); - } + Service<InterfaceManager>.GetNullable()?.SetImmersiveMode(b); } catch (Exception ex) { diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/EnumSettingsEntry{T}.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/EnumSettingsEntry{T}.cs index 096e408b8..8fb91940e 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/EnumSettingsEntry{T}.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/EnumSettingsEntry{T}.cs @@ -2,6 +2,8 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using CheapLoc; + using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/ThirdRepoSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/ThirdRepoSettingsEntry.cs index daa91420f..5737b44db 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/ThirdRepoSettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/ThirdRepoSettingsEntry.cs @@ -5,7 +5,6 @@ using System.Numerics; using System.Threading.Tasks; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Configuration; using Dalamud.Configuration.Internal; diff --git a/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs b/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs index 4add874ba..f9e8022a1 100644 --- a/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs +++ b/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs @@ -1,9 +1,9 @@ +using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Reflection; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; @@ -12,7 +12,6 @@ using Dalamud.Interface.Style; using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using Dalamud.Utility; - using Serilog; namespace Dalamud.Interface.Internal.Windows.StyleEditor; @@ -51,7 +50,7 @@ public class StyleEditorWindow : Window this.didSave = false; var config = Service<DalamudConfiguration>.Get(); - config.SavedStyles ??= []; + config.SavedStyles ??= new List<StyleModel>(); this.currentSel = config.SavedStyles.FindIndex(x => x.Name == config.ChosenStyle); this.initialStyle = config.ChosenStyle; diff --git a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs index 9d4f7ab04..e3eb22a04 100644 --- a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs +++ b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs @@ -22,13 +22,12 @@ using Dalamud.Plugin.Internal; using Dalamud.Plugin.Services; using Dalamud.Storage.Assets; using Dalamud.Utility; - using FFXIVClientStructs.FFXIV.Component.GUI; - using Lumina.Text.ReadOnly; - using Serilog; +using LSeStringBuilder = Lumina.Text.SeStringBuilder; + namespace Dalamud.Interface.Internal.Windows; /// <summary> @@ -50,9 +49,9 @@ internal class TitleScreenMenuWindow : Window, IDisposable private readonly Lazy<IDalamudTextureWrap> shadeTexture; private readonly AddonLifecycleEventListener versionStringListener; - private readonly Dictionary<Guid, InOutCubic> shadeEasings = []; - private readonly Dictionary<Guid, InOutQuint> moveEasings = []; - private readonly Dictionary<Guid, InOutCubic> logoEasings = []; + private readonly Dictionary<Guid, InOutCubic> shadeEasings = new(); + private readonly Dictionary<Guid, InOutQuint> moveEasings = new(); + private readonly Dictionary<Guid, InOutCubic> logoEasings = new(); private readonly IConsoleVariable<bool> showTsm; @@ -87,8 +86,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable : base( "TitleScreenMenuOverlay", ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar | - ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNavFocus | - ImGuiWindowFlags.NoDocking) + ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNavFocus) { this.showTsm = consoleManager.AddVariable("dalamud.show_tsm", "Show the Title Screen Menu", true); @@ -473,9 +471,9 @@ internal class TitleScreenMenuWindow : Window, IDisposable private unsafe void OnVersionStringDraw(AddonEvent ev, AddonArgs args) { - if (ev is not (AddonEvent.PostDraw or AddonEvent.PreDraw)) return; + if (args is not AddonDrawArgs drawArgs) return; - var addon = args.Addon.Struct; + var addon = drawArgs.Addon.Struct; var textNode = addon->GetTextNodeById(3); // look and feel init. should be harmless to set. @@ -499,23 +497,20 @@ internal class TitleScreenMenuWindow : Window, IDisposable return; this.lastLoadedPluginCount = count; - using var rssb = new RentedSeStringBuilder(); - - rssb.Builder - .Append(new ReadOnlySeStringSpan(addon->AtkValues[1].String.Value)) - .Append("\n\n") - .PushEdgeColorType(701) - .PushColorType(539) + var lssb = LSeStringBuilder.SharedPool.Get(); + lssb.Append(new ReadOnlySeStringSpan(addon->AtkValues[1].String.Value)).Append("\n\n"); + lssb.PushEdgeColorType(701).PushColorType(539) .Append(SeIconChar.BoxedLetterD.ToIconChar()) - .PopColorType() - .PopEdgeColorType() - .Append($" Dalamud: {Versioning.GetScmVersion()}") - .Append($" - {count} {(count != 1 ? "plugins" : "plugin")} loaded"); + .PopColorType().PopEdgeColorType(); + lssb.Append($" Dalamud: {Util.GetScmVersion()}"); + + lssb.Append($" - {count} {(count != 1 ? "plugins" : "plugin")} loaded"); if (pm?.SafeMode is true) - rssb.Builder.PushColorType(17).Append(" [SAFE MODE]").PopColorType(); + lssb.PushColorType(17).Append(" [SAFE MODE]").PopColorType(); - textNode->SetText(rssb.Builder.GetViewAsSpan()); + textNode->SetText(lssb.GetViewAsSpan()); + LSeStringBuilder.SharedPool.Return(lssb); } private void TitleScreenMenuEntryListChange() => this.privateAtlas.BuildFontsAsync(); diff --git a/Dalamud/Interface/ManagedFontAtlas/FontAtlasBuildToolkitUtilities.cs b/Dalamud/Interface/ManagedFontAtlas/FontAtlasBuildToolkitUtilities.cs index b2b2c2ab1..2e497b6cd 100644 --- a/Dalamud/Interface/ManagedFontAtlas/FontAtlasBuildToolkitUtilities.cs +++ b/Dalamud/Interface/ManagedFontAtlas/FontAtlasBuildToolkitUtilities.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using System.Text.Unicode; using Dalamud.Bindings.ImGui; +using Dalamud.Interface.Utility; namespace Dalamud.Interface.ManagedFontAtlas; diff --git a/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs index be2f5a742..2853aa4d2 100644 --- a/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs +++ b/Dalamud/Interface/ManagedFontAtlas/IFontHandle.cs @@ -1,5 +1,4 @@ -using System.Threading; -using System.Threading.Tasks; +using System.Threading.Tasks; using Dalamud.Bindings.ImGui; @@ -34,22 +33,10 @@ public interface IFontHandle : IDisposable /// </summary> /// <remarks> /// Use <see cref="Push"/> directly if you want to keep the current ImGui font if the font is not ready.<br /> - /// Alternatively, use <see cref="WaitAsync()"/> to wait for this property to become <c>true</c>. + /// Alternatively, use <see cref="WaitAsync"/> to wait for this property to become <c>true</c>. /// </remarks> bool Available { get; } - /// <summary> - /// Attempts to lock the fully constructed instance of <see cref="ImFontPtr"/> corresponding to the this - /// <see cref="IFontHandle"/>, for use in any thread.<br /> - /// Modification of the font will exhibit undefined behavior if some other thread also uses the font. - /// </summary> - /// <param name="errorMessage">The error message, if any.</param> - /// <returns> - /// An instance of <see cref="ILockedImFont"/> that <b>must</b> be disposed after use on success; - /// <c>null</c> with <paramref name="errorMessage"/> populated on failure. - /// </returns> - ILockedImFont? TryLock(out string? errorMessage); - /// <summary> /// Locks the fully constructed instance of <see cref="ImFontPtr"/> corresponding to the this /// <see cref="IFontHandle"/>, for use in any thread.<br /> @@ -105,11 +92,4 @@ public interface IFontHandle : IDisposable /// </summary> /// <returns>A task containing this <see cref="IFontHandle"/>.</returns> Task<IFontHandle> WaitAsync(); - - /// <summary> - /// Waits for <see cref="Available"/> to become <c>true</c>. - /// </summary> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>A task containing this <see cref="IFontHandle"/>.</returns> - Task<IFontHandle> WaitAsync(CancellationToken cancellationToken); } diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs index 0e2f503b4..f2c91d264 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using System.Threading; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Utility; @@ -35,8 +34,8 @@ internal sealed class DelegateFontHandle : FontHandle /// </summary> internal sealed class HandleManager : IFontHandleManager { - private readonly HashSet<DelegateFontHandle> handles = []; - private readonly Lock syncRoot = new(); + private readonly HashSet<DelegateFontHandle> handles = new(); + private readonly object syncRoot = new(); /// <summary> /// Initializes a new instance of the <see cref="HandleManager"/> class. @@ -96,8 +95,8 @@ internal sealed class DelegateFontHandle : FontHandle private static readonly ModuleLog Log = new($"{nameof(DelegateFontHandle)}.{nameof(HandleSubstance)}"); // Owned by this class, but ImFontPtr values still do not belong to this. - private readonly Dictionary<DelegateFontHandle, ImFontPtr> fonts = []; - private readonly Dictionary<DelegateFontHandle, Exception?> buildExceptions = []; + private readonly Dictionary<DelegateFontHandle, ImFontPtr> fonts = new(); + private readonly Dictionary<DelegateFontHandle, Exception?> buildExceptions = new(); /// <summary> /// Initializes a new instance of the <see cref="HandleSubstance"/> class. diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs index 97dc29804..2a93cf093 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.BuildToolkit.cs @@ -15,7 +15,7 @@ using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Utility; using Dalamud.Storage.Assets; using Dalamud.Utility; - +using SharpDX.DXGI; using TerraFX.Interop.DirectX; namespace Dalamud.Interface.ManagedFontAtlas.Internals; @@ -25,7 +25,8 @@ namespace Dalamud.Interface.ManagedFontAtlas.Internals; /// </summary> internal sealed partial class FontAtlasFactory { - private static readonly Dictionary<ulong, List<(char Left, char Right, float Distance)>> PairAdjustmentsCache = []; + private static readonly Dictionary<ulong, List<(char Left, char Right, float Distance)>> PairAdjustmentsCache = + new(); /// <summary> /// Implementations for <see cref="IFontAtlasBuildToolkitPreBuild"/> and @@ -43,7 +44,7 @@ internal sealed partial class FontAtlasFactory private readonly GamePrebakedFontHandle.HandleSubstance gameFontHandleSubstance; private readonly FontAtlasFactory factory; private readonly FontAtlasBuiltData data; - private readonly List<Action> registeredPostBuildActions = []; + private readonly List<Action> registeredPostBuildActions = new(); /// <summary> /// Initializes a new instance of the <see cref="BuildToolkit"/> class. @@ -85,7 +86,7 @@ internal sealed partial class FontAtlasFactory /// <summary> /// Gets the font scale modes. /// </summary> - private Dictionary<ImFontPtr, FontScaleMode> FontScaleModes { get; } = []; + private Dictionary<ImFontPtr, FontScaleMode> FontScaleModes { get; } = new(); /// <inheritdoc/> public void Dispose() => this.disposeAfterBuild.Dispose(); @@ -170,7 +171,7 @@ internal sealed partial class FontAtlasFactory }; if (fontConfig.GlyphRanges is not { Length: > 0 } ranges) - ranges = [1, 0xFFFE, 0]; + ranges = new ushort[] { 1, 0xFFFE, 0 }; raw.GlyphRanges = (ushort*)this.DisposeAfterBuild( GCHandle.Alloc(ranges, GCHandleType.Pinned)).AddrOfPinnedObject(); @@ -188,7 +189,7 @@ internal sealed partial class FontAtlasFactory { if (!PairAdjustmentsCache.TryGetValue(hashIdent, out pairAdjustments)) { - PairAdjustmentsCache.Add(hashIdent, pairAdjustments = []); + PairAdjustmentsCache.Add(hashIdent, pairAdjustments = new()); try { pairAdjustments.AddRange(TrueTypeUtils.ExtractHorizontalPairAdjustments(raw).ToArray()); @@ -382,7 +383,7 @@ internal sealed partial class FontAtlasFactory DalamudAsset.FontAwesomeFreeSolid, fontConfig with { - GlyphRanges = [FontAwesomeIconMin, FontAwesomeIconMax, 0], + GlyphRanges = new ushort[] { FontAwesomeIconMin, FontAwesomeIconMax, 0 }, }); /// <inheritdoc/> @@ -391,12 +392,12 @@ internal sealed partial class FontAtlasFactory DalamudAsset.LodestoneGameSymbol, fontConfig with { - GlyphRanges = - [ + GlyphRanges = new ushort[] + { GamePrebakedFontHandle.SeIconCharMin, GamePrebakedFontHandle.SeIconCharMax, 0, - ], + }, }); /// <inheritdoc/> @@ -629,7 +630,7 @@ internal sealed partial class FontAtlasFactory { this.AddDalamudAssetFont( DalamudAsset.NotoSansJpMedium, - new() { GlyphRanges = [' ', ' ', '\0'], SizePx = 1 }); + new() { GlyphRanges = new ushort[] { ' ', ' ', '\0' }, SizePx = 1 }); } if (!this.NewImAtlas.Build()) @@ -748,7 +749,7 @@ internal sealed partial class FontAtlasFactory new( width, height, - (int)(use4 ? DXGI_FORMAT.DXGI_FORMAT_B4G4R4A4_UNORM : DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM), + (int)(use4 ? Format.B4G4R4A4_UNorm : Format.B8G8R8A8_UNorm), width * bpp), buf, name); diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.Implementation.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.Implementation.cs index 323d4173d..430f26127 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.Implementation.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.Implementation.cs @@ -1,4 +1,4 @@ -// #define VeryVerboseLog +// #define VeryVerboseLog using System.Collections.Generic; using System.Diagnostics; @@ -41,9 +41,9 @@ internal sealed partial class FontAtlasFactory /// <summary> /// If set, disables concurrent font build operation. /// </summary> - private static readonly Lock? NoConcurrentBuildOperationLock = null; // new(); + private static readonly object? NoConcurrentBuildOperationLock = null; // new(); - private static readonly ModuleLog Log = ModuleLog.Create<FontAtlasFactory>(); + private static readonly ModuleLog Log = new(nameof(FontAtlasFactory)); private static readonly Task<FontAtlasBuiltData> EmptyTask = Task.FromResult(default(FontAtlasBuiltData)); @@ -66,10 +66,10 @@ internal sealed partial class FontAtlasFactory try { - var substancesList = this.substances = []; + var substancesList = this.substances = new(); this.Garbage.Add(() => substancesList.Clear()); - var wrapsCopy = this.wraps = []; + var wrapsCopy = this.wraps = new(); this.Garbage.Add(() => wrapsCopy.Clear()); var atlasPtr = ImGui.ImFontAtlas(); @@ -115,14 +115,16 @@ internal sealed partial class FontAtlasFactory public void AddExistingTexture(IDalamudTextureWrap wrap) { - ObjectDisposedException.ThrowIf(this.wraps == null, this); + if (this.wraps is null) + throw new ObjectDisposedException(nameof(FontAtlasBuiltData)); this.wraps.Add(this.Garbage.Add(wrap)); } public int AddNewTexture(IDalamudTextureWrap wrap, bool disposeOnError) { - ObjectDisposedException.ThrowIf(this.wraps == null, this); + if (this.wraps is null) + throw new ObjectDisposedException(nameof(FontAtlasBuiltData)); var handle = wrap.Handle; var index = this.ImTextures.IndexOf(x => x.TexID == handle); @@ -252,7 +254,7 @@ internal sealed partial class FontAtlasFactory private readonly GamePrebakedFontHandle.HandleManager gameFontHandleManager; private readonly IFontHandleManager[] fontHandleManagers; - private readonly Lock syncRoot = new(); + private readonly object syncRoot = new(); private Task<FontAtlasBuiltData?> buildTask = EmptyTask; private FontAtlasBuiltData? builtData; @@ -290,13 +292,13 @@ internal sealed partial class FontAtlasFactory this.factory.InterfaceManager.AfterBuildFonts += this.OnRebuildRecommend; this.disposables.Add(() => this.factory.InterfaceManager.AfterBuildFonts -= this.OnRebuildRecommend); - this.fontHandleManagers = - [ + this.fontHandleManagers = new IFontHandleManager[] + { this.delegateFontHandleManager = this.disposables.Add( new DelegateFontHandle.HandleManager(atlasName)), this.gameFontHandleManager = this.disposables.Add( new GamePrebakedFontHandle.HandleManager(atlasName, factory)), - ]; + }; foreach (var fhm in this.fontHandleManagers) fhm.RebuildRecommend += this.OnRebuildRecommend; } diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.cs index 55c2acdbc..6ae810dec 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.cs @@ -18,9 +18,7 @@ using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Plugin.Internal.Types; using Dalamud.Storage.Assets; using Dalamud.Utility; - using Lumina.Data.Files; - using TerraFX.Interop.DirectX; namespace Dalamud.Interface.ManagedFontAtlas.Internals; diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontHandle.cs index ce67b0eec..1fdaf4596 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontHandle.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontHandle.cs @@ -10,7 +10,6 @@ using Dalamud.Interface.Utility; using Dalamud.Plugin.Internal; using Dalamud.Plugin.Internal.Types; using Dalamud.Utility; - using Serilog; namespace Dalamud.Interface.ManagedFontAtlas.Internals; @@ -21,7 +20,7 @@ namespace Dalamud.Interface.ManagedFontAtlas.Internals; internal abstract class FontHandle : IFontHandle { private const int NonMainThreadFontAccessWarningCheckInterval = 10000; - private static readonly ConditionalWeakTable<LocalPlugin, object> NonMainThreadFontAccessWarning = []; + private static readonly ConditionalWeakTable<LocalPlugin, object> NonMainThreadFontAccessWarning = new(); private static long nextNonMainThreadFontAccessWarningCheck; private readonly List<IDisposable> pushedFonts = new(8); @@ -239,17 +238,12 @@ internal abstract class FontHandle : IFontHandle } /// <inheritdoc/> - public Task<IFontHandle> WaitAsync() => this.WaitAsync(CancellationToken.None); - - /// <inheritdoc/> - public Task<IFontHandle> WaitAsync(CancellationToken cancellationToken) + public Task<IFontHandle> WaitAsync() { if (this.Available) return Task.FromResult<IFontHandle>(this); var tcs = new TaskCompletionSource<IFontHandle>(TaskCreationOptions.RunContinuationsAsynchronously); - cancellationToken.Register(() => tcs.TrySetCanceled()); - this.ImFontChanged += OnImFontChanged; this.Disposed += OnDisposed; if (this.Available) diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs index 81c7d4d89..f6904db7c 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs @@ -4,15 +4,14 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reactive.Disposables; -using System.Threading; using Dalamud.Bindings.ImGui; using Dalamud.Game.Text; using Dalamud.Interface.GameFonts; +using Dalamud.Interface.Internal; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Utility; using Dalamud.Utility; - using Lumina.Data.Files; using Vector4 = System.Numerics.Vector4; @@ -102,9 +101,9 @@ internal class GamePrebakedFontHandle : FontHandle /// </summary> internal sealed class HandleManager : IFontHandleManager { - private readonly Dictionary<GameFontStyle, int> gameFontsRc = []; - private readonly HashSet<GamePrebakedFontHandle> handles = []; - private readonly Lock syncRoot = new(); + private readonly Dictionary<GameFontStyle, int> gameFontsRc = new(); + private readonly HashSet<GamePrebakedFontHandle> handles = new(); + private readonly object syncRoot = new(); /// <summary> /// Initializes a new instance of the <see cref="HandleManager"/> class. @@ -189,11 +188,11 @@ internal class GamePrebakedFontHandle : FontHandle private readonly HashSet<GameFontStyle> gameFontStyles; // Owned by this class, but ImFontPtr values still do not belong to this. - private readonly Dictionary<GameFontStyle, FontDrawPlan> fonts = []; - private readonly Dictionary<GameFontStyle, Exception?> buildExceptions = []; - private readonly List<(ImFontPtr Font, GameFontStyle Style, ushort[]? Ranges)> attachments = []; + private readonly Dictionary<GameFontStyle, FontDrawPlan> fonts = new(); + private readonly Dictionary<GameFontStyle, Exception?> buildExceptions = new(); + private readonly List<(ImFontPtr Font, GameFontStyle Style, ushort[]? Ranges)> attachments = new(); - private readonly HashSet<ImFontPtr> templatedFonts = []; + private readonly HashSet<ImFontPtr> templatedFonts = new(); /// <summary> /// Initializes a new instance of the <see cref="HandleSubstance"/> class. @@ -416,7 +415,7 @@ internal class GamePrebakedFontHandle : FontHandle DalamudAsset.NotoSansJpMedium, new() { - GlyphRanges = [' ', ' ', '\0'], + GlyphRanges = new ushort[] { ' ', ' ', '\0' }, SizePx = sizePx, }); this.templatedFonts.Add(font); @@ -450,8 +449,8 @@ internal class GamePrebakedFontHandle : FontHandle public readonly GameFontStyle BaseStyle; public readonly GameFontFamilyAndSizeAttribute BaseAttr; public readonly int TexCount; - public readonly Dictionary<ImFontPtr, BitArray> Ranges = []; - public readonly List<(int RectId, int FdtGlyphIndex)> Rects = []; + public readonly Dictionary<ImFontPtr, BitArray> Ranges = new(); + public readonly List<(int RectId, int FdtGlyphIndex)> Rects = new(); public readonly ushort[] RectLookup = new ushort[0x10000]; public readonly FdtFileView Fdt; public readonly ImFontPtr FullRangeFont; diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/LockedImFont.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/LockedImFont.cs index 9eb90fe16..b7f5ad12b 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/LockedImFont.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/LockedImFont.cs @@ -34,7 +34,8 @@ internal class LockedImFont : ILockedImFont /// <inheritdoc/> public ILockedImFont NewRef() { - ObjectDisposedException.ThrowIf(this.owner == null, this); + if (this.owner is null) + throw new ObjectDisposedException(nameof(LockedImFont)); var newRef = new LockedImFont(this.ImFont, this.owner); this.owner.AddRef(); diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/SimplePushedFont.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/SimplePushedFont.cs index ed40ce28b..0d6ad5c7c 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/SimplePushedFont.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/SimplePushedFont.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using Dalamud.Bindings.ImGui; +using Dalamud.Interface.Utility; using Microsoft.Extensions.ObjectPool; diff --git a/Dalamud/Interface/Style/DalamudColors.cs b/Dalamud/Interface/Style/DalamudColors.cs index e4a8c5b9b..aa3339c19 100644 --- a/Dalamud/Interface/Style/DalamudColors.cs +++ b/Dalamud/Interface/Style/DalamudColors.cs @@ -1,7 +1,6 @@ -using System.Numerics; +using System.Numerics; using Dalamud.Interface.Colors; - using Newtonsoft.Json; namespace Dalamud.Interface.Style; diff --git a/Dalamud/Interface/Style/StyleModel.cs b/Dalamud/Interface/Style/StyleModel.cs index 4c64e3a21..bfce480f2 100644 --- a/Dalamud/Interface/Style/StyleModel.cs +++ b/Dalamud/Interface/Style/StyleModel.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -6,9 +6,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; using Dalamud.Utility; - using Newtonsoft.Json; - using Serilog; namespace Dalamud.Interface.Style; @@ -70,7 +68,7 @@ public abstract class StyleModel /// <exception cref="ArgumentException">Thrown in case the version of the model is not known.</exception> public static StyleModel? Deserialize(string model) { - var json = Util.DecompressString(Convert.FromBase64String(model[3..])); + var json = Util.DecompressString(Convert.FromBase64String(model.Substring(3))); if (model.StartsWith(StyleModelV1.SerializedPrefix)) return JsonConvert.DeserializeObject<StyleModelV1>(json); @@ -88,7 +86,8 @@ public abstract class StyleModel if (configuration.SavedStylesOld == null) return; - configuration.SavedStyles = [.. configuration.SavedStylesOld]; + configuration.SavedStyles = new List<StyleModel>(); + configuration.SavedStyles.AddRange(configuration.SavedStylesOld); Log.Information("Transferred {NumStyles} styles", configuration.SavedStyles.Count); @@ -103,11 +102,16 @@ public abstract class StyleModel /// <exception cref="ArgumentOutOfRangeException">Thrown when the version of the style model is unknown.</exception> public string Serialize() { - var prefix = this switch + string prefix; + switch (this) { - StyleModelV1 => StyleModelV1.SerializedPrefix, - _ => throw new ArgumentOutOfRangeException(), - }; + case StyleModelV1: + prefix = StyleModelV1.SerializedPrefix; + break; + default: + throw new ArgumentOutOfRangeException(); + } + return prefix + Convert.ToBase64String(Util.CompressString(JsonConvert.SerializeObject(this))); } diff --git a/Dalamud/Interface/Style/StyleModelV1.cs b/Dalamud/Interface/Style/StyleModelV1.cs index 4af2512fd..8c1de86f3 100644 --- a/Dalamud/Interface/Style/StyleModelV1.cs +++ b/Dalamud/Interface/Style/StyleModelV1.cs @@ -3,7 +3,6 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Colors; - using Newtonsoft.Json; namespace Dalamud.Interface.Style; @@ -18,7 +17,7 @@ public class StyleModelV1 : StyleModel /// </summary> private StyleModelV1() { - this.Colors = []; + this.Colors = new Dictionary<string, Vector4>(); this.Name = "Unknown"; } @@ -397,7 +396,7 @@ public class StyleModelV1 : StyleModel model.SelectableTextAlign = style.SelectableTextAlign; model.DisplaySafeAreaPadding = style.DisplaySafeAreaPadding; - model.Colors = []; + model.Colors = new Dictionary<string, Vector4>(); foreach (var imGuiCol in Enum.GetValues<ImGuiCol>()) { diff --git a/Dalamud/Interface/Textures/ForwardingSharedImmediateTexture.cs b/Dalamud/Interface/Textures/ForwardingSharedImmediateTexture.cs index 45dc69bbd..12e312b3e 100644 --- a/Dalamud/Interface/Textures/ForwardingSharedImmediateTexture.cs +++ b/Dalamud/Interface/Textures/ForwardingSharedImmediateTexture.cs @@ -1,7 +1,8 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Dalamud.Interface.Textures.TextureWraps; +using Dalamud.Storage.Assets; namespace Dalamud.Interface.Textures; diff --git a/Dalamud/Interface/Textures/ISharedImmediateTexture.cs b/Dalamud/Interface/Textures/ISharedImmediateTexture.cs index 7f3b54c97..b6aa4da83 100644 --- a/Dalamud/Interface/Textures/ISharedImmediateTexture.cs +++ b/Dalamud/Interface/Textures/ISharedImmediateTexture.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; +using Dalamud.Interface.Internal; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Utility; diff --git a/Dalamud/Interface/Textures/ImGuiViewportTextureArgs.cs b/Dalamud/Interface/Textures/ImGuiViewportTextureArgs.cs index 248fbbc67..d04688fe4 100644 --- a/Dalamud/Interface/Textures/ImGuiViewportTextureArgs.cs +++ b/Dalamud/Interface/Textures/ImGuiViewportTextureArgs.cs @@ -2,7 +2,9 @@ using System.Numerics; using System.Text; using Dalamud.Bindings.ImGui; +using Dalamud.Interface.Internal; using Dalamud.Interface.Textures.TextureWraps; +using TerraFX.Interop.DirectX; namespace Dalamud.Interface.Textures; diff --git a/Dalamud/Interface/Textures/Internal/BitmapCodecInfo.cs b/Dalamud/Interface/Textures/Internal/BitmapCodecInfo.cs index 7f5ed4fda..3d5456500 100644 --- a/Dalamud/Interface/Textures/Internal/BitmapCodecInfo.cs +++ b/Dalamud/Interface/Textures/Internal/BitmapCodecInfo.cs @@ -44,12 +44,12 @@ internal sealed class BitmapCodecInfo : IBitmapCodecInfo private static unsafe string ReadStringUsing( IWICBitmapCodecInfo* codecInfo, - delegate* unmanaged[MemberFunction]<IWICBitmapCodecInfo*, uint, char*, uint*, int> readFuncPtr) + delegate* unmanaged<IWICBitmapCodecInfo*, uint, ushort*, uint*, int> readFuncPtr) { var cch = 0u; _ = readFuncPtr(codecInfo, 0, null, &cch); var buf = stackalloc char[(int)cch + 1]; - Marshal.ThrowExceptionForHR(readFuncPtr(codecInfo, cch + 1, buf, &cch)); - return new string(buf, 0, (int)cch).Trim('\0'); + Marshal.ThrowExceptionForHR(readFuncPtr(codecInfo, cch + 1, (ushort*)buf, &cch)); + return new(buf, 0, (int)cch); } } diff --git a/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/GamePathSharedImmediateTexture.cs b/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/GamePathSharedImmediateTexture.cs index a320d921e..185ae07b9 100644 --- a/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/GamePathSharedImmediateTexture.cs +++ b/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/GamePathSharedImmediateTexture.cs @@ -70,7 +70,7 @@ internal sealed class GamePathSharedImmediateTexture : SharedImmediateTexture } cancellationToken.ThrowIfCancellationRequested(); - var wrap = tm.NoThrottleCreateFromTexFile(file.Header, file.TextureBuffer); + var wrap = tm.NoThrottleCreateFromTexFile(file); tm.BlameSetName(wrap, this.ToString()); return wrap; } diff --git a/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/ManifestResourceSharedImmediateTexture.cs b/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/ManifestResourceSharedImmediateTexture.cs index 511f6e110..c95a9b0ad 100644 --- a/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/ManifestResourceSharedImmediateTexture.cs +++ b/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/ManifestResourceSharedImmediateTexture.cs @@ -3,6 +3,7 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; +using Dalamud.Interface.Internal; using Dalamud.Interface.Textures.TextureWraps; namespace Dalamud.Interface.Textures.Internal.SharedImmediateTextures; @@ -35,7 +36,10 @@ internal sealed class ManifestResourceSharedImmediateTexture : SharedImmediateTe /// <inheritdoc/> protected override async Task<IDalamudTextureWrap> CreateTextureAsync(CancellationToken cancellationToken) { - await using var stream = this.assembly.GetManifestResourceStream(this.name) ?? throw new FileNotFoundException("The resource file could not be found."); + await using var stream = this.assembly.GetManifestResourceStream(this.name); + if (stream is null) + throw new FileNotFoundException("The resource file could not be found."); + var tm = await Service<TextureManager>.GetAsync(); var ms = new MemoryStream(stream.CanSeek ? checked((int)stream.Length) : 0); await stream.CopyToAsync(ms, cancellationToken); diff --git a/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/SharedImmediateTexture.cs b/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/SharedImmediateTexture.cs index 931a1a73b..5f9925ed3 100644 --- a/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/SharedImmediateTexture.cs +++ b/Dalamud/Interface/Textures/Internal/SharedImmediateTextures/SharedImmediateTexture.cs @@ -23,8 +23,8 @@ internal abstract class SharedImmediateTexture private static long instanceCounter; - private readonly Lock reviveLock = new(); - private readonly List<LocalPlugin> ownerPlugins = []; + private readonly object reviveLock = new(); + private readonly List<LocalPlugin> ownerPlugins = new(); private bool resourceReleased; private int refCount; @@ -476,8 +476,8 @@ internal abstract class SharedImmediateTexture { var ownerCopy = this.owner; var wrapCopy = this.innerWrap; - - ObjectDisposedException.ThrowIf(ownerCopy is null || wrapCopy is null, this); + if (ownerCopy is null || wrapCopy is null) + throw new ObjectDisposedException(nameof(RefCountableWrappingTextureWrap)); ownerCopy.AddRef(); return new RefCountableWrappingTextureWrap(wrapCopy, ownerCopy); diff --git a/Dalamud/Interface/Textures/Internal/TextureManager.BlameTracker.cs b/Dalamud/Interface/Textures/Internal/TextureManager.BlameTracker.cs index ed1824e5c..837b41271 100644 --- a/Dalamud/Interface/Textures/Internal/TextureManager.BlameTracker.cs +++ b/Dalamud/Interface/Textures/Internal/TextureManager.BlameTracker.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Dalamud.Bindings.ImGui; +using Dalamud.Interface.Internal; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Services; @@ -43,7 +44,7 @@ internal sealed partial class TextureManager /// <summary>Gets the list containing all the loaded textures from plugins.</summary> /// <remarks>Returned value must be used inside a lock.</remarks> - public List<IBlameableDalamudTextureWrap> BlameTracker { get; } = []; + public List<IBlameableDalamudTextureWrap> BlameTracker { get; } = new(); /// <summary>Gets the blame for a texture wrap.</summary> /// <param name="textureWrap">The texture wrap.</param> @@ -218,14 +219,14 @@ internal sealed partial class TextureManager return; - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static int QueryInterfaceStatic(IUnknown* pThis, Guid* riid, void** ppvObject) => ToManagedObject(pThis)?.QueryInterface(riid, ppvObject) ?? E.E_UNEXPECTED; - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static uint AddRefStatic(IUnknown* pThis) => (uint)(ToManagedObject(pThis)?.AddRef() ?? 0); - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static uint ReleaseStatic(IUnknown* pThis) => (uint)(ToManagedObject(pThis)?.Release() ?? 0); } @@ -233,7 +234,7 @@ internal sealed partial class TextureManager public static Guid* NativeGuid => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in MyGuid)); /// <inheritdoc/> - public List<LocalPlugin> OwnerPlugins { get; } = []; + public List<LocalPlugin> OwnerPlugins { get; } = new(); /// <inheritdoc/> public nint ResourceAddress => (nint)this.tex2D; diff --git a/Dalamud/Interface/Textures/Internal/TextureManager.Clipboard.cs b/Dalamud/Interface/Textures/Internal/TextureManager.Clipboard.cs index 75f7ab975..8a510e967 100644 --- a/Dalamud/Interface/Textures/Internal/TextureManager.Clipboard.cs +++ b/Dalamud/Interface/Textures/Internal/TextureManager.Clipboard.cs @@ -133,7 +133,7 @@ internal sealed partial class TextureManager }, }, }; - namea.AsSpan().CopyTo(new(Unsafe.AsPointer(ref fgda.fgd.e0.cFileName[0]), 260)); + namea.AsSpan().CopyTo(new(fgda.fgd.e0.cFileName, 260)); AddToDataObject( pdo, @@ -157,7 +157,7 @@ internal sealed partial class TextureManager }, }, }; - preferredFileNameWithoutExtension.AsSpan().CopyTo(new(Unsafe.AsPointer(ref fgdw.fgd.e0.cFileName[0]), 260)); + preferredFileNameWithoutExtension.AsSpan().CopyTo(new(fgdw.fgd.e0.cFileName, 260)); AddToDataObject( pdo, @@ -450,7 +450,7 @@ internal sealed partial class TextureManager try { IStream* pfs; - SHCreateStreamOnFileW((char*)pPath, sharedRead, &pfs).ThrowOnError(); + SHCreateStreamOnFileW((ushort*)pPath, sharedRead, &pfs).ThrowOnError(); var stgm2 = new STGMEDIUM { diff --git a/Dalamud/Interface/Textures/Internal/TextureManager.Drawer.cs b/Dalamud/Interface/Textures/Internal/TextureManager.Drawer.cs index e803a1d13..b4573f04f 100644 --- a/Dalamud/Interface/Textures/Internal/TextureManager.Drawer.cs +++ b/Dalamud/Interface/Textures/Internal/TextureManager.Drawer.cs @@ -5,7 +5,6 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Storage.Assets; using Dalamud.Utility; - using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; diff --git a/Dalamud/Interface/Textures/Internal/TextureManager.FromSeString.cs b/Dalamud/Interface/Textures/Internal/TextureManager.FromSeString.cs deleted file mode 100644 index 3e90ae3ea..000000000 --- a/Dalamud/Interface/Textures/Internal/TextureManager.FromSeString.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Dalamud.Interface.ImGuiSeStringRenderer; -using Dalamud.Interface.ImGuiSeStringRenderer.Internal; -using Dalamud.Interface.Textures.TextureWraps; -using Dalamud.Utility; - -namespace Dalamud.Interface.Textures.Internal; - -/// <summary>Service responsible for loading and disposing ImGui texture wraps.</summary> -internal sealed partial class TextureManager -{ - [ServiceManager.ServiceDependency] - private readonly SeStringRenderer seStringRenderer = Service<SeStringRenderer>.Get(); - - /// <inheritdoc/> - public IDalamudTextureWrap CreateTextureFromSeString( - ReadOnlySpan<byte> text, - scoped in SeStringDrawParams drawParams = default, - string? debugName = null) - { - ThreadSafety.AssertMainThread(); - using var dd = this.seStringRenderer.CreateDrawData(text, drawParams); - var texture = this.CreateDrawListTexture(debugName ?? nameof(this.CreateTextureFromSeString)); - try - { - texture.Size = dd.Data.DisplaySize; - texture.Draw(dd.DataPtr); - return texture; - } - catch - { - texture.Dispose(); - throw; - } - } -} diff --git a/Dalamud/Interface/Textures/Internal/TextureManager.GamePath.cs b/Dalamud/Interface/Textures/Internal/TextureManager.GamePath.cs index fe0f390eb..0796ad6cc 100644 --- a/Dalamud/Interface/Textures/Internal/TextureManager.GamePath.cs +++ b/Dalamud/Interface/Textures/Internal/TextureManager.GamePath.cs @@ -113,8 +113,8 @@ internal sealed partial class TextureManager var format = highResolution ? HighResolutionIconFileFormat : IconFileFormat; type ??= string.Empty; - if (type.Length > 0 && !type.EndsWith('/')) - type += '/'; + if (type.Length > 0 && !type.EndsWith("/")) + type += "/"; return string.Format(format, iconId / 1000, type, iconId); } diff --git a/Dalamud/Interface/Textures/Internal/TextureManager.SharedTextures.cs b/Dalamud/Interface/Textures/Internal/TextureManager.SharedTextures.cs index 85cf3a1ca..d7e185b68 100644 --- a/Dalamud/Interface/Textures/Internal/TextureManager.SharedTextures.cs +++ b/Dalamud/Interface/Textures/Internal/TextureManager.SharedTextures.cs @@ -5,6 +5,7 @@ using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; +using System.Threading.Tasks; using BitFaster.Caching.Lru; @@ -64,7 +65,7 @@ internal sealed partial class TextureManager private readonly ConcurrentDictionary<string, SharedImmediateTexture> gameDict = new(); private readonly ConcurrentDictionary<string, SharedImmediateTexture> fileDict = new(); private readonly ConcurrentDictionary<(Assembly, string), SharedImmediateTexture> manifestResourceDict = new(); - private readonly HashSet<SharedImmediateTexture> invalidatedTextures = []; + private readonly HashSet<SharedImmediateTexture> invalidatedTextures = new(); private readonly Thread sharedTextureReleaseThread; diff --git a/Dalamud/Interface/Textures/Internal/TextureManager.cs b/Dalamud/Interface/Textures/Internal/TextureManager.cs index 22a257395..059c716ce 100644 --- a/Dalamud/Interface/Textures/Internal/TextureManager.cs +++ b/Dalamud/Interface/Textures/Internal/TextureManager.cs @@ -19,7 +19,6 @@ using Dalamud.Utility.TerraFxCom; using Lumina.Data; using Lumina.Data.Files; -using Lumina.Data.Parsing.Tex.Buffers; using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; @@ -34,7 +33,7 @@ internal sealed partial class TextureManager ITextureSubstitutionProvider, ITextureReadbackProvider { - private static readonly ModuleLog Log = ModuleLog.Create<TextureManager>(); + private static readonly ModuleLog Log = new(nameof(TextureManager)); [ServiceManager.ServiceDependency] private readonly Dalamud dalamud = Service<Dalamud>.Get(); @@ -219,7 +218,7 @@ internal sealed partial class TextureManager null, _ => Task.FromResult( this.BlameSetName( - this.NoThrottleCreateFromTexFile(file.Header, file.TextureBuffer), + this.NoThrottleCreateFromTexFile(file), debugName ?? $"{nameof(this.CreateFromTexFile)}({ForceNullable(file.FilePath)?.Path})")), cancellationToken); @@ -249,7 +248,7 @@ internal sealed partial class TextureManager usage = D3D11_USAGE.D3D11_USAGE_DYNAMIC; else usage = D3D11_USAGE.D3D11_USAGE_DEFAULT; - + using var texture = this.device.CreateTexture2D( new() { @@ -345,14 +344,14 @@ internal sealed partial class TextureManager /// <summary>Creates a texture from the given <see cref="TexFile"/>. Skips the load throttler; intended to be used /// from implementation of <see cref="SharedImmediateTexture"/>s.</summary> - /// <param name="header">Header of a <c>.tex</c> file.</param> - /// <param name="buffer">Texture buffer.</param> + /// <param name="file">The data.</param> /// <returns>The loaded texture.</returns> - internal IDalamudTextureWrap NoThrottleCreateFromTexFile(TexFile.TexHeader header, TextureBuffer buffer) + internal IDalamudTextureWrap NoThrottleCreateFromTexFile(TexFile file) { ObjectDisposedException.ThrowIf(this.disposeCts.IsCancellationRequested, this); - var (dxgiFormat, conversion) = TexFile.GetDxgiFormatFromTextureFormat(header.Format, false); + var buffer = file.TextureBuffer; + var (dxgiFormat, conversion) = TexFile.GetDxgiFormatFromTextureFormat(file.Header.Format, false); if (conversion != TexFile.DxgiFormatConversion.NoConversion || !this.IsDxgiFormatSupported((DXGI_FORMAT)dxgiFormat)) { @@ -361,31 +360,34 @@ internal sealed partial class TextureManager } var wrap = this.NoThrottleCreateFromRaw(new(buffer.Width, buffer.Height, dxgiFormat), buffer.RawData); - this.BlameSetName(wrap, $"{nameof(this.NoThrottleCreateFromTexFile)}({header.Width} x {header.Height})"); + this.BlameSetName(wrap, $"{nameof(this.NoThrottleCreateFromTexFile)}({ForceNullable(file.FilePath).Path})"); return wrap; + + static T? ForceNullable<T>(T s) => s; } /// <summary>Creates a texture from the given <paramref name="fileBytes"/>, trying to interpret it as a /// <see cref="TexFile"/>.</summary> /// <param name="fileBytes">The file bytes.</param> /// <returns>The loaded texture.</returns> - internal unsafe IDalamudTextureWrap NoThrottleCreateFromTexFile(ReadOnlySpan<byte> fileBytes) + internal IDalamudTextureWrap NoThrottleCreateFromTexFile(ReadOnlySpan<byte> fileBytes) { ObjectDisposedException.ThrowIf(this.disposeCts.IsCancellationRequested, this); if (!TexFileExtensions.IsPossiblyTexFile2D(fileBytes)) throw new InvalidDataException("The file is not a TexFile."); - TexFile.TexHeader header; - TextureBuffer buffer; - fixed (byte* p = fileBytes) - { - var lbr = new LuminaBinaryReader(new UnmanagedMemoryStream(p, fileBytes.Length)); - header = lbr.ReadStructure<TexFile.TexHeader>(); - buffer = TextureBuffer.FromStream(header, lbr); - } + var bytesArray = fileBytes.ToArray(); + var tf = new TexFile(); + typeof(TexFile).GetProperty(nameof(tf.Data))!.GetSetMethod(true)!.Invoke( + tf, + new object?[] { bytesArray }); + typeof(TexFile).GetProperty(nameof(tf.Reader))!.GetSetMethod(true)!.Invoke( + tf, + new object?[] { new LuminaBinaryReader(bytesArray) }); + // Note: FileInfo and FilePath are not used from TexFile; skip it. - var wrap = this.NoThrottleCreateFromTexFile(header, buffer); + var wrap = this.NoThrottleCreateFromTexFile(tf); this.BlameSetName(wrap, $"{nameof(this.NoThrottleCreateFromTexFile)}({fileBytes.Length:n0})"); return wrap; } diff --git a/Dalamud/Interface/Textures/Internal/TextureManagerPluginScoped.cs b/Dalamud/Interface/Textures/Internal/TextureManagerPluginScoped.cs index ac6de7dd7..9b0fa0943 100644 --- a/Dalamud/Interface/Textures/Internal/TextureManagerPluginScoped.cs +++ b/Dalamud/Interface/Textures/Internal/TextureManagerPluginScoped.cs @@ -6,7 +6,6 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Dalamud.Interface.ImGuiSeStringRenderer; using Dalamud.Interface.Internal; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.IoC; @@ -284,18 +283,6 @@ internal sealed class TextureManagerPluginScoped return textureWrap; } - /// <inheritdoc/> - public IDalamudTextureWrap CreateTextureFromSeString( - ReadOnlySpan<byte> text, - scoped in SeStringDrawParams drawParams = default, - string? debugName = null) - { - var manager = this.ManagerOrThrow; - var textureWrap = manager.CreateTextureFromSeString(text, drawParams, debugName); - manager.Blame(textureWrap, this.plugin); - return textureWrap; - } - /// <inheritdoc/> public IEnumerable<IBitmapCodecInfo> GetSupportedImageDecoderInfos() => this.ManagerOrThrow.Wic.GetSupportedDecoderInfos(); diff --git a/Dalamud/Interface/Textures/TextureWraps/ForwardingTextureWrap.cs b/Dalamud/Interface/Textures/TextureWraps/ForwardingTextureWrap.cs index 2cb1deac5..342397c5e 100644 --- a/Dalamud/Interface/Textures/TextureWraps/ForwardingTextureWrap.cs +++ b/Dalamud/Interface/Textures/TextureWraps/ForwardingTextureWrap.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using Dalamud.Bindings.ImGui; +using Dalamud.Interface.Internal; using Dalamud.Interface.Textures.TextureWraps.Internal; using TerraFX.Interop.Windows; diff --git a/Dalamud/Interface/Textures/TextureWraps/Internal/ViewportTextureWrap.cs b/Dalamud/Interface/Textures/TextureWraps/Internal/ViewportTextureWrap.cs index d4407d76a..3e0f31eca 100644 --- a/Dalamud/Interface/Textures/TextureWraps/Internal/ViewportTextureWrap.cs +++ b/Dalamud/Interface/Textures/TextureWraps/Internal/ViewportTextureWrap.cs @@ -9,7 +9,6 @@ using Dalamud.Interface.Textures.Internal; using Dalamud.Plugin.Internal.Types; using Dalamud.Storage.Assets; using Dalamud.Utility; - using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; diff --git a/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs b/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs index cf849a1ef..586d65559 100644 --- a/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs +++ b/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -22,7 +22,7 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu /// </summary> internal const uint TextureSize = 64; - private readonly List<TitleScreenMenuEntry> entries = []; + private readonly List<TitleScreenMenuEntry> entries = new(); private TitleScreenMenuEntry[]? entriesView; [ServiceManager.ServiceConstructor] @@ -42,7 +42,7 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu { lock (this.entries) { - if (this.entries.Count == 0) + if (!this.entries.Any()) return Array.Empty<TitleScreenMenuEntry>(); return this.entriesView ??= this.entries.OrderByDescending(x => x.IsInternal).ToArray(); @@ -59,7 +59,7 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu { lock (this.entries) { - if (this.entries.Count == 0) + if (!this.entries.Any()) return Array.Empty<TitleScreenMenuEntry>(); return this.entriesView ??= this.entries.OrderByDescending(x => x.IsInternal).ToArray(); @@ -81,7 +81,7 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu lock (this.entries) { var entriesOfAssembly = this.entries.Where(x => x.CallingAssembly == Assembly.GetCallingAssembly()).ToList(); - var priority = entriesOfAssembly.Count != 0 + var priority = entriesOfAssembly.Any() ? unchecked(entriesOfAssembly.Select(x => x.Priority).Max() + 1) : 0; entry = new(Assembly.GetCallingAssembly(), priority, text, texture, onTriggered); @@ -191,7 +191,7 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu lock (this.entries) { var entriesOfAssembly = this.entries.Where(x => x.CallingAssembly == null).ToList(); - var priority = entriesOfAssembly.Count != 0 + var priority = entriesOfAssembly.Any() ? unchecked(entriesOfAssembly.Select(x => x.Priority).Max() + 1) : 0; entry = new(null, priority, text, texture, onTriggered, showConditionKeys) @@ -220,7 +220,7 @@ internal class TitleScreenMenuPluginScoped : IInternalDisposableService, ITitleS [ServiceManager.ServiceDependency] private readonly TitleScreenMenu titleScreenMenuService = Service<TitleScreenMenu>.Get(); - private readonly List<IReadOnlyTitleScreenMenuEntry> pluginEntries = []; + private readonly List<IReadOnlyTitleScreenMenuEntry> pluginEntries = new(); /// <inheritdoc/> public IReadOnlyList<IReadOnlyTitleScreenMenuEntry>? Entries => this.titleScreenMenuService.Entries; diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs index d2b4b655e..e38537018 100644 --- a/Dalamud/Interface/UiBuilder.cs +++ b/Dalamud/Interface/UiBuilder.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Threading; using System.Threading.Tasks; using Dalamud.Bindings.ImGui; @@ -13,9 +12,9 @@ using Dalamud.Interface.FontIdentifier; using Dalamud.Interface.Internal; using Dalamud.Interface.ManagedFontAtlas; using Dalamud.Interface.ManagedFontAtlas.Internals; +using Dalamud.Plugin; using Dalamud.Plugin.Internal.Types; using Dalamud.Utility; - using Serilog; namespace Dalamud.Interface; @@ -151,6 +150,13 @@ public interface IUiBuilder /// </summary> public ImFontPtr FontMono { get; } + /// <summary> + /// Gets the game's active Direct3D device. + /// </summary> + // TODO: Remove it on API11/APIXI, and remove SharpDX/PInvoke/etc. dependency from Dalamud. + [Obsolete($"Use {nameof(DeviceHandle)} and wrap it using DirectX wrapper library of your choice.")] + SharpDX.Direct3D11.Device Device { get; } + /// <summary>Gets the game's active Direct3D device.</summary> /// <value>Pointer to the instance of IUnknown that the game is using and should be containing an ID3D11Device, /// or 0 if it is not available yet.</value> @@ -220,12 +226,6 @@ public interface IUiBuilder /// </summary> bool ShouldUseReducedMotion { get; } - /// <summary> - /// Gets a value indicating whether the user has enabled the "Enable sound effects for plugin windows" setting.<br /> - /// This setting is effected by the in-game "System Sounds" option and volume. - /// </summary> - bool PluginUISoundEffectsEnabled { get; } - /// <summary> /// Loads an ULD file that can load textures containing multiple icons in a single texture. /// </summary> @@ -302,6 +302,8 @@ public sealed class UiBuilder : IDisposable, IUiBuilder private IFontHandle? monoFontHandle; private IFontHandle? iconFontFixedWidthHandle; + private SharpDX.Direct3D11.Device? sdxDevice; + /// <summary> /// Initializes a new instance of the <see cref="UiBuilder"/> class and registers it. /// You do not have to call this manually. @@ -491,6 +493,12 @@ public sealed class UiBuilder : IDisposable, IUiBuilder this.InterfaceManagerWithScene?.MonoFontHandle ?? throw new InvalidOperationException("Scene is not yet ready."))); + /// <inheritdoc/> + // TODO: Remove it on API11/APIXI, and remove SharpDX/PInvoke/etc. dependency from Dalamud. + [Obsolete($"Use {nameof(DeviceHandle)} and wrap it using DirectX wrapper library of your choice.")] + public SharpDX.Direct3D11.Device Device => + this.sdxDevice ??= new(this.InterfaceManagerWithScene!.Backend!.DeviceHandle); + /// <inheritdoc/> public nint DeviceHandle => this.InterfaceManagerWithScene?.Backend?.DeviceHandle ?? 0; @@ -567,9 +575,6 @@ public sealed class UiBuilder : IDisposable, IUiBuilder /// </summary> public bool ShouldUseReducedMotion => Service<DalamudConfiguration>.Get().ReduceMotions ?? false; - /// <inheritdoc /> - public bool PluginUISoundEffectsEnabled => Service<DalamudConfiguration>.Get().EnablePluginUISoundEffects; - /// <summary> /// Gets or sets a value indicating whether statistics about UI draw time should be collected. /// </summary> @@ -602,7 +607,7 @@ public sealed class UiBuilder : IDisposable, IUiBuilder /// <summary> /// Gets or sets a history of the last draw times, used to calculate an average. /// </summary> - internal List<long> DrawTimeHistory { get; set; } = []; + internal List<long> DrawTimeHistory { get; set; } = new List<long>(); private InterfaceManager? InterfaceManagerWithScene => Service<InterfaceManager.InterfaceManagerWithScene>.GetNullable()?.Manager; @@ -686,14 +691,13 @@ public sealed class UiBuilder : IDisposable, IUiBuilder FontAtlasAutoRebuildMode autoRebuildMode, bool isGlobalScaled = true, string? debugName = null) => - this.scopedFinalizer.Add( - Service<FontAtlasFactory> - .Get() - .CreateFontAtlas( - this.namespaceName + ":" + (debugName ?? "custom"), - autoRebuildMode, - isGlobalScaled, - this.plugin)); + this.scopedFinalizer.Add(Service<FontAtlasFactory> + .Get() + .CreateFontAtlas( + this.namespaceName + ":" + (debugName ?? "custom"), + autoRebuildMode, + isGlobalScaled, + this.plugin)); /// <summary> /// Unregister the UiBuilder. Do not call this in plugin code. @@ -864,15 +868,6 @@ public sealed class UiBuilder : IDisposable, IUiBuilder // Note: do not dispose w; we do not own it } - public ILockedImFont? TryLock(out string? errorMessage) - { - if (this.wrapped is { } w) - return w.TryLock(out errorMessage); - - errorMessage = nameof(ObjectDisposedException); - return null; - } - public ILockedImFont Lock() => this.wrapped?.Lock() ?? throw new ObjectDisposedException(nameof(FontHandleWrapper)); @@ -881,13 +876,7 @@ public sealed class UiBuilder : IDisposable, IUiBuilder public void Pop() => this.WrappedNotDisposed.Pop(); public Task<IFontHandle> WaitAsync() => - this.wrapped?.WaitAsync().ContinueWith(_ => (IFontHandle)this) - ?? Task.FromException<IFontHandle>(new ObjectDisposedException(nameof(FontHandleWrapper))); - - public Task<IFontHandle> WaitAsync(CancellationToken cancellationToken) => - this.wrapped?.WaitAsync(cancellationToken) - .ContinueWith(_ => (IFontHandle)this, cancellationToken) - ?? Task.FromException<IFontHandle>(new ObjectDisposedException(nameof(FontHandleWrapper))); + this.WrappedNotDisposed.WaitAsync().ContinueWith(_ => (IFontHandle)this); public override string ToString() => $"{nameof(FontHandleWrapper)}({this.wrapped?.ToString() ?? "disposed"})"; diff --git a/Dalamud/Interface/UldWrapper.cs b/Dalamud/Interface/UldWrapper.cs index 48c6a114d..85a2d8344 100644 --- a/Dalamud/Interface/UldWrapper.cs +++ b/Dalamud/Interface/UldWrapper.cs @@ -7,7 +7,6 @@ using Dalamud.Interface.Textures; using Dalamud.Interface.Textures.Internal; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Utility; - using Lumina.Data.Files; using Lumina.Data.Parsing.Uld; @@ -18,7 +17,7 @@ public class UldWrapper : IDisposable { private readonly DataManager data; private readonly TextureManager textureManager; - private readonly Dictionary<string, (uint Id, int Width, int Height, bool HD, byte[] RgbaData)> textures = []; + private readonly Dictionary<string, (uint Id, int Width, int Height, bool HD, byte[] RgbaData)> textures = new(); /// <summary> Initializes a new instance of the <see cref="UldWrapper"/> class, wrapping an ULD file. </summary> /// <param name="uiBuilder">The UiBuilder used to load textures.</param> diff --git a/Dalamud/Interface/Utility/BufferBackedImDrawData.cs b/Dalamud/Interface/Utility/BufferBackedImDrawData.cs deleted file mode 100644 index e6128992a..000000000 --- a/Dalamud/Interface/Utility/BufferBackedImDrawData.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -using Dalamud.Bindings.ImGui; - -namespace Dalamud.Interface.Utility; - -/// <summary>Wrapper aroundx <see cref="ImDrawData"/> containing one <see cref="ImDrawList"/>.</summary> -public unsafe struct BufferBackedImDrawData : IDisposable -{ - private nint buffer; - - /// <summary>Initializes a new instance of the <see cref="BufferBackedImDrawData"/> struct.</summary> - /// <param name="buffer">Address of buffer to use.</param> - private BufferBackedImDrawData(nint buffer) => this.buffer = buffer; - - /// <summary>Gets the <see cref="ImDrawData"/> stored in this buffer.</summary> - public readonly ref ImDrawData Data => ref ((DataStruct*)this.buffer)->Data; - - /// <summary>Gets the <see cref="ImDrawDataPtr"/> stored in this buffer.</summary> - public readonly ImDrawDataPtr DataPtr => new((ImDrawData*)Unsafe.AsPointer(ref this.Data)); - - /// <summary>Gets the <see cref="ImDrawList"/> stored in this buffer.</summary> - public readonly ref ImDrawList List => ref ((DataStruct*)this.buffer)->List; - - /// <summary>Gets the <see cref="ImDrawListPtr"/> stored in this buffer.</summary> - public readonly ImDrawListPtr ListPtr => new((ImDrawList*)Unsafe.AsPointer(ref this.List)); - - /// <summary>Creates a new instance of <see cref="BufferBackedImDrawData"/>.</summary> - /// <returns>A new instance of <see cref="BufferBackedImDrawData"/>.</returns> - public static BufferBackedImDrawData Create() - { - if (ImGui.GetCurrentContext().IsNull || ImGui.GetIO().FontDefault.Handle is null) - throw new("ImGui is not ready"); - - var res = new BufferBackedImDrawData(Marshal.AllocHGlobal(sizeof(DataStruct))); - var ds = (DataStruct*)res.buffer; - *ds = default; - - var atlas = ImGui.GetIO().Fonts; - ds->SharedData = *ImGui.GetDrawListSharedData().Handle; - ds->SharedData.TexIdCommon = atlas.Textures[atlas.TextureIndexCommon].TexID; - ds->SharedData.TexUvWhitePixel = atlas.TexUvWhitePixel; - ds->SharedData.TexUvLines = (Vector4*)Unsafe.AsPointer(ref atlas.TexUvLines[0]); - ds->SharedData.Font = ImGui.GetIO().FontDefault; - ds->SharedData.FontSize = ds->SharedData.Font->FontSize; - ds->SharedData.ClipRectFullscreen = new( - float.NegativeInfinity, - float.NegativeInfinity, - float.PositiveInfinity, - float.PositiveInfinity); - - ds->List.Data = &ds->SharedData; - ds->ListPtr = &ds->List; - ds->Data.CmdLists = &ds->ListPtr; - ds->Data.CmdListsCount = 1; - ds->Data.FramebufferScale = Vector2.One; - - res.ListPtr._ResetForNewFrame(); - res.ListPtr.PushClipRectFullScreen(); - res.ListPtr.PushTextureID(new(atlas.TextureIndexCommon)); - return res; - } - - /// <summary>Updates the statistics information stored in <see cref="DataPtr"/> from <see cref="ListPtr"/>.</summary> - public readonly void UpdateDrawDataStatistics() - { - this.Data.TotalIdxCount = this.List.IdxBuffer.Size; - this.Data.TotalVtxCount = this.List.VtxBuffer.Size; - } - - /// <inheritdoc/> - public void Dispose() - { - if (this.buffer != 0) - { - this.ListPtr._ClearFreeMemory(); - Marshal.FreeHGlobal(this.buffer); - this.buffer = 0; - } - } - - [StructLayout(LayoutKind.Sequential)] - private struct DataStruct - { - public ImDrawData Data; - public ImDrawList* ListPtr; - public ImDrawList List; - public ImDrawListSharedData SharedData; - } -} diff --git a/Dalamud/Interface/Utility/ImGuiHelpers.cs b/Dalamud/Interface/Utility/ImGuiHelpers.cs index ee2840d3d..87f258250 100644 --- a/Dalamud/Interface/Utility/ImGuiHelpers.cs +++ b/Dalamud/Interface/Utility/ImGuiHelpers.cs @@ -575,15 +575,6 @@ public static partial class ImGuiHelpers public static unsafe ImFontPtr OrElse(this ImFontPtr self, ImFontPtr other) => self.IsNull ? other : self; - /// <summary>Creates a draw data that will draw the given SeString onto it.</summary> - /// <param name="sss">SeString to render.</param> - /// <param name="style">Initial rendering style.</param> - /// <returns>A new self-contained draw data.</returns> - internal static BufferBackedImDrawData CreateDrawData( - ReadOnlySpan<byte> sss, - scoped in SeStringDrawParams style = default) => - Service<SeStringRenderer>.Get().CreateDrawData(sss, style); - /// <summary> /// Mark 4K page as used, after adding a codepoint to a font. /// </summary> diff --git a/Dalamud/Interface/Utility/Internal/DevTextureSaveMenu.cs b/Dalamud/Interface/Utility/Internal/DevTextureSaveMenu.cs index 7fcf795aa..86435e8c1 100644 --- a/Dalamud/Interface/Utility/Internal/DevTextureSaveMenu.cs +++ b/Dalamud/Interface/Utility/Internal/DevTextureSaveMenu.cs @@ -6,17 +6,13 @@ using System.Text; using System.Threading.Tasks; using Dalamud.Bindings.ImGui; -using Dalamud.Game; using Dalamud.Interface.ImGuiFileDialog; using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification.Internal; using Dalamud.Interface.Internal; -using Dalamud.Interface.Internal.Windows.Data.Widgets; using Dalamud.Interface.Textures.Internal; using Dalamud.Interface.Textures.TextureWraps; - using Serilog; - using TerraFX.Interop.Windows; namespace Dalamud.Interface.Utility.Internal; @@ -37,14 +33,6 @@ internal sealed class DevTextureSaveMenu : IInternalDisposableService this.interfaceManager.Draw += this.InterfaceManagerOnDraw; } - private enum ContextMenuActionType - { - None, - SaveAsFile, - CopyToClipboard, - SendToTexWidget, - } - /// <inheritdoc/> void IInternalDisposableService.DisposeService() => this.interfaceManager.Draw -= this.InterfaceManagerOnDraw; @@ -78,16 +66,15 @@ internal sealed class DevTextureSaveMenu : IInternalDisposableService var textureManager = await Service<TextureManager>.GetAsync(); var popupName = $"{nameof(this.ShowTextureSaveMenuAsync)}_{textureWrap.Handle.Handle:X}"; - ContextMenuActionType action; BitmapCodecInfo? encoder; { var first = true; var encoders = textureManager.Wic.GetSupportedEncoderInfos().ToList(); - var tcs = new TaskCompletionSource<(ContextMenuActionType Action, BitmapCodecInfo? Codec)>( + var tcs = new TaskCompletionSource<BitmapCodecInfo?>( TaskCreationOptions.RunContinuationsAsynchronously); Service<InterfaceManager>.Get().Draw += DrawChoices; - (action, encoder) = await tcs.Task; + encoder = await tcs.Task; [SuppressMessage("ReSharper", "AccessToDisposedClosure", Justification = "This shall not escape")] void DrawChoices() @@ -111,20 +98,13 @@ internal sealed class DevTextureSaveMenu : IInternalDisposableService } if (ImGui.Selectable("Copy"u8)) - tcs.TrySetResult((ContextMenuActionType.CopyToClipboard, null)); - if (ImGui.Selectable("Send to TexWidget"u8)) - tcs.TrySetResult((ContextMenuActionType.SendToTexWidget, null)); - - ImGui.Separator(); - + tcs.TrySetResult(null); foreach (var encoder2 in encoders) { if (ImGui.Selectable(encoder2.Name)) - tcs.TrySetResult((ContextMenuActionType.SaveAsFile, encoder2)); + tcs.TrySetResult(encoder2); } - ImGui.Separator(); - const float previewImageWidth = 320; var size = textureWrap.Size; if (size.X > previewImageWidth) @@ -140,68 +120,50 @@ internal sealed class DevTextureSaveMenu : IInternalDisposableService } } - switch (action) + if (encoder is null) { - case ContextMenuActionType.CopyToClipboard: - isCopy = true; - await textureManager.CopyToClipboardAsync(textureWrap, name, true); - break; + isCopy = true; + await textureManager.CopyToClipboardAsync(textureWrap, name, true); + } + else + { + var props = new Dictionary<string, object>(); + if (encoder.ContainerGuid == GUID.GUID_ContainerFormatTiff) + props["CompressionQuality"] = 1.0f; + else if (encoder.ContainerGuid == GUID.GUID_ContainerFormatJpeg || + encoder.ContainerGuid == GUID.GUID_ContainerFormatHeif || + encoder.ContainerGuid == GUID.GUID_ContainerFormatWmp) + props["ImageQuality"] = 1.0f; - case ContextMenuActionType.SendToTexWidget: - { - var framework = await Service<Framework>.GetAsync(); - var dalamudInterface = await Service<DalamudInterface>.GetAsync(); - await framework.RunOnFrameworkThread( - () => - { - var texWidget = dalamudInterface.GetDataWindowWidget<TexWidget>(); - dalamudInterface.SetDataWindowWidget(texWidget); - texWidget.AddTexture(Task.FromResult(textureWrap.CreateWrapSharingLowLevelResource())); - }); - break; - } - - case ContextMenuActionType.SaveAsFile when encoder is not null: - { - var props = new Dictionary<string, object>(); - if (encoder.ContainerGuid == GUID.GUID_ContainerFormatTiff) - props["CompressionQuality"] = 1.0f; - else if (encoder.ContainerGuid == GUID.GUID_ContainerFormatJpeg || - encoder.ContainerGuid == GUID.GUID_ContainerFormatHeif || - encoder.ContainerGuid == GUID.GUID_ContainerFormatWmp) - props["ImageQuality"] = 1.0f; - - var tcs = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously); - this.fileDialogManager.SaveFileDialog( - "Save texture...", - $"{encoder.Name.Replace(',', '.')}{{{string.Join(',', encoder.Extensions)}}}", - name + encoder.Extensions.First(), - encoder.Extensions.First(), - (ok, path2) => - { - if (!ok) - tcs.SetCanceled(); - else - tcs.SetResult(path2); - }); - var path = await tcs.Task.ConfigureAwait(false); - - await textureManager.SaveToFileAsync(textureWrap, encoder.ContainerGuid, path, props: props); - - var notif = Service<NotificationManager>.Get().AddNotification( - new() - { - Content = $"File saved to: {path}", - Title = initiatorName, - Type = NotificationType.Success, - }); - notif.Click += n => + var tcs = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously); + this.fileDialogManager.SaveFileDialog( + "Save texture...", + $"{encoder.Name.Replace(',', '.')}{{{string.Join(',', encoder.Extensions)}}}", + name + encoder.Extensions.First(), + encoder.Extensions.First(), + (ok, path2) => { - Process.Start(new ProcessStartInfo(path) { UseShellExecute = true }); - n.Notification.DismissNow(); - }; - break; - } + if (!ok) + tcs.SetCanceled(); + else + tcs.SetResult(path2); + }); + var path = await tcs.Task.ConfigureAwait(false); + + await textureManager.SaveToFileAsync(textureWrap, encoder.ContainerGuid, path, props: props); + + var notif = Service<NotificationManager>.Get().AddNotification( + new() + { + Content = $"File saved to: {path}", + Title = initiatorName, + Type = NotificationType.Success, + }); + notif.Click += n => + { + Process.Start(new ProcessStartInfo(path) { UseShellExecute = true }); + n.Notification.DismissNow(); + }; } } catch (Exception e) diff --git a/Dalamud/Interface/Utility/Raii/Color.cs b/Dalamud/Interface/Utility/Raii/Color.cs index 9682b929e..7bf2efc38 100644 --- a/Dalamud/Interface/Utility/Raii/Color.cs +++ b/Dalamud/Interface/Utility/Raii/Color.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Numerics; using Dalamud.Bindings.ImGui; +using Dalamud.Utility; namespace Dalamud.Interface.Utility.Raii; @@ -28,7 +29,7 @@ public static partial class ImRaii public sealed class Color : IDisposable { - internal static readonly List<(ImGuiCol, uint)> Stack = []; + internal static readonly List<(ImGuiCol, uint)> Stack = new(); private int count; public Color Push(ImGuiCol idx, uint color, bool condition = true) diff --git a/Dalamud/Interface/Utility/Raii/EndObjects.cs b/Dalamud/Interface/Utility/Raii/EndObjects.cs index 80ae495e2..80122360c 100644 --- a/Dalamud/Interface/Utility/Raii/EndObjects.cs +++ b/Dalamud/Interface/Utility/Raii/EndObjects.cs @@ -193,8 +193,8 @@ public static partial class ImRaii return new EndUnconditionally(Restore, true); } - private static EndUnconditionally DisabledEnd() - => new(() => + private static IEndObject DisabledEnd() + => new EndUnconditionally(() => { --disabledCount; ImGui.EndDisabled(); diff --git a/Dalamud/Interface/Utility/Raii/Font.cs b/Dalamud/Interface/Utility/Raii/Font.cs index 1b5e8cc58..da35c693a 100644 --- a/Dalamud/Interface/Utility/Raii/Font.cs +++ b/Dalamud/Interface/Utility/Raii/Font.cs @@ -1,4 +1,5 @@ using Dalamud.Bindings.ImGui; +using Dalamud.Utility; namespace Dalamud.Interface.Utility.Raii; diff --git a/Dalamud/Interface/Utility/Raii/Plot.cs b/Dalamud/Interface/Utility/Raii/Plot.cs index e88919e48..d2ff38299 100644 --- a/Dalamud/Interface/Utility/Raii/Plot.cs +++ b/Dalamud/Interface/Utility/Raii/Plot.cs @@ -96,7 +96,7 @@ public static partial class ImRaii public sealed class PlotStyle : IDisposable { - internal static readonly List<(ImPlotStyleVar, Vector2)> Stack = []; + internal static readonly List<(ImPlotStyleVar, Vector2)> Stack = new(); private int count; @@ -249,7 +249,7 @@ public static partial class ImRaii public sealed class PlotColor : IDisposable { - internal static readonly List<(ImPlotCol, uint)> Stack = []; + internal static readonly List<(ImPlotCol, uint)> Stack = new(); private int count; // Reimplementation of https://github.com/ocornut/imgui/blob/868facff9ded2d61425c67deeba354eb24275bd1/imgui.cpp#L3035 diff --git a/Dalamud/Interface/Utility/Raii/Style.cs b/Dalamud/Interface/Utility/Raii/Style.cs index e178b68d3..bfd04ea3c 100644 --- a/Dalamud/Interface/Utility/Raii/Style.cs +++ b/Dalamud/Interface/Utility/Raii/Style.cs @@ -35,7 +35,7 @@ public static partial class ImRaii public sealed class Style : IDisposable { - internal static readonly List<(ImGuiStyleVar, Vector2)> Stack = []; + internal static readonly List<(ImGuiStyleVar, Vector2)> Stack = new(); private int count; diff --git a/Dalamud/Interface/Windowing/Persistence/PresetModel.cs b/Dalamud/Interface/Windowing/Persistence/PresetModel.cs index 1c6a93c16..f7910e0b2 100644 --- a/Dalamud/Interface/Windowing/Persistence/PresetModel.cs +++ b/Dalamud/Interface/Windowing/Persistence/PresetModel.cs @@ -25,7 +25,7 @@ internal class PresetModel /// Gets or sets a dictionary containing the windows in the preset, mapping their ID to the preset. /// </summary> [JsonProperty("w")] - public Dictionary<uint, PresetWindow> Windows { get; set; } = []; + public Dictionary<uint, PresetWindow> Windows { get; set; } = new(); /// <summary> /// Class representing a window in a preset. @@ -53,7 +53,6 @@ internal class PresetModel /// <summary> /// Gets a value indicating whether this preset is in the default state. /// </summary> - [JsonIgnore] public bool IsDefault => !this.IsPinned && !this.IsClickThrough && diff --git a/Dalamud/Interface/Windowing/Window.cs b/Dalamud/Interface/Windowing/Window.cs index dab3506c0..44ff62199 100644 --- a/Dalamud/Interface/Windowing/Window.cs +++ b/Dalamud/Interface/Windowing/Window.cs @@ -1,17 +1,17 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Numerics; +using System.Runtime.InteropServices; using System.Threading.Tasks; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Keys; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; using Dalamud.Interface.Internal; -using Dalamud.Interface.Internal.Windows.StyleEditor; using Dalamud.Interface.Textures.Internal; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Utility; @@ -19,13 +19,10 @@ using Dalamud.Interface.Utility.Internal; using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing.Persistence; using Dalamud.Logging.Internal; +using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.UI; -using TerraFX.Interop.Windows; - -using static TerraFX.Interop.Windows.Windows; - namespace Dalamud.Interface.Windowing; /// <summary> @@ -34,15 +31,11 @@ namespace Dalamud.Interface.Windowing; public abstract class Window { private const float FadeInOutTime = 0.072f; - private const string AdditionsPopupName = "WindowSystemContextActions"; - private static readonly ModuleLog Log = ModuleLog.Create<WindowSystem>(); + private static readonly ModuleLog Log = new("WindowSystem"); private static bool wasEscPressedLastFrame = false; - private readonly TitleBarButton additionsButton; - private readonly List<TitleBarButton> allButtons = []; - private bool internalLastIsOpen = false; private bool internalIsOpen = false; private bool internalIsPinned = false; @@ -79,20 +72,6 @@ public abstract class Window this.WindowName = name; this.Flags = flags; this.ForceMainWindow = forceMainWindow; - - this.additionsButton = new() - { - Icon = FontAwesomeIcon.Bars, - IconOffset = new Vector2(2.5f, 1), - Click = _ => - { - this.internalIsClickthrough = false; - this.presetDirty = true; - ImGui.OpenPopup(AdditionsPopupName); - }, - Priority = int.MinValue, - AvailableClickthrough = true, - }; } /// <summary> @@ -247,16 +226,6 @@ public abstract class Window /// </summary> public bool AllowClickthrough { get; set; } = true; - /// <summary> - /// Gets a value indicating whether this window is pinned. - /// </summary> - public bool IsPinned => this.internalIsPinned; - - /// <summary> - /// Gets a value indicating whether this window is click-through. - /// </summary> - public bool IsClickthrough => this.internalIsClickthrough; - /// <summary> /// Gets or sets a list of available title bar buttons. /// @@ -264,7 +233,7 @@ public abstract class Window /// disabled globally by the user, an internal title bar button to manage these is added when drawing, but it will /// not appear in this collection. If you wish to remove this button, set both of these values to false. /// </summary> - public List<TitleBarButton> TitleBarButtons { get; set; } = []; + public List<TitleBarButton> TitleBarButtons { get; set; } = new(); /// <summary> /// Gets or sets a value indicating whether this window will stay open. @@ -446,23 +415,14 @@ public abstract class Window UIGlobals.PlaySoundEffect(this.OnOpenSfxId); } - var isErrorStylePushed = false; - if (!this.hasError) - { - this.PreDraw(); - this.ApplyConditionals(); - } - else - { - Style.StyleModelV1.DalamudStandard.Push(); - isErrorStylePushed = true; - } + this.PreDraw(); + this.ApplyConditionals(); if (this.ForceMainWindow) ImGuiHelpers.ForceNextWindowMainViewport(); var wasFocused = this.IsFocused; - if (wasFocused && this is not StyleEditorWindow) + if (wasFocused) { var style = ImGui.GetStyle(); var focusedHeaderColor = style.Colors[(int)ImGuiCol.TitleBgActive]; @@ -478,22 +438,10 @@ public abstract class Window var flags = this.Flags; if (this.internalIsPinned || this.internalIsClickthrough) - { flags |= ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize; - } if (this.internalIsClickthrough) - { flags |= ImGuiWindowFlags.NoInputs | ImGuiWindowFlags.NoNav | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoMouseInputs; - } - - // If we have an error, reset all flags to default, and unlock window size. - if (this.hasError) - { - flags = ImGuiWindowFlags.None; - ImGui.SetNextWindowCollapsed(false, ImGuiCond.Once); - ImGui.SetNextWindowSizeConstraints(Vector2.Zero, Vector2.PositiveInfinity); - } if (this.CanShowCloseButton ? ImGui.Begin(this.WindowName, ref this.internalIsOpen, flags) : ImGui.Begin(this.WindowName, flags)) { @@ -503,12 +451,14 @@ public abstract class Window ImGuiP.GetCurrentWindow().InheritNoInputs = this.internalIsClickthrough; } - if (ImGui.GetWindowViewport().ID != ImGui.GetMainViewport().ID) + // Not supported yet on non-main viewports + if ((this.internalIsPinned || this.internalIsClickthrough || this.internalAlpha.HasValue) && + ImGui.GetWindowViewport().ID != ImGui.GetMainViewport().ID) { - if ((flags & ImGuiWindowFlags.NoInputs) == ImGuiWindowFlags.NoInputs) - ImGui.GetWindowViewport().Flags |= ImGuiViewportFlags.NoInputs; - else - ImGui.GetWindowViewport().Flags &= ~ImGuiViewportFlags.NoInputs; + this.internalAlpha = null; + this.internalIsPinned = false; + this.internalIsClickthrough = false; + this.presetDirty = true; } if (this.hasError) @@ -532,6 +482,7 @@ public abstract class Window } } + const string additionsPopupName = "WindowSystemContextActions"; var flagsApplicableForTitleBarIcons = !flags.HasFlag(ImGuiWindowFlags.NoDecoration) && !flags.HasFlag(ImGuiWindowFlags.NoTitleBar); var showAdditions = (this.AllowPinning || this.AllowClickthrough) && @@ -542,8 +493,13 @@ public abstract class Window { ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f); - if (ImGui.BeginPopup(AdditionsPopupName, ImGuiWindowFlags.NoMove)) + if (ImGui.BeginPopup(additionsPopupName, ImGuiWindowFlags.NoMove)) { + var isAvailable = ImGuiHelpers.CheckIsWindowOnMainViewport(); + + if (!isAvailable) + ImGui.BeginDisabled(); + if (this.internalIsClickthrough) ImGui.BeginDisabled(); @@ -591,11 +547,21 @@ public abstract class Window this.presetDirty = true; } - ImGui.TextColored( - ImGuiColors.DalamudGrey, - Loc.Localize( - "WindowSystemContextActionClickthroughDisclaimer", - "Open this menu again by clicking the three dashes to disable clickthrough.")); + if (isAvailable) + { + ImGui.TextColored(ImGuiColors.DalamudGrey, + Loc.Localize("WindowSystemContextActionClickthroughDisclaimer", + "Open this menu again by clicking the three dashes to disable clickthrough.")); + } + else + { + ImGui.TextColored(ImGuiColors.DalamudGrey, + Loc.Localize("WindowSystemContextActionViewportDisclaimer", + "These features are only available if this window is inside the game window.")); + } + + if (!isAvailable) + ImGui.EndDisabled(); if (ImGui.Button(Loc.Localize("WindowSystemContextActionPrintWindow", "Print window"))) printWindow = true; @@ -606,18 +572,37 @@ public abstract class Window ImGui.PopStyleVar(); } - if (flagsApplicableForTitleBarIcons) + unsafe { - this.allButtons.Clear(); - this.allButtons.EnsureCapacity(this.TitleBarButtons.Count + 1); - this.allButtons.AddRange(this.TitleBarButtons); - if (showAdditions) - this.allButtons.Add(this.additionsButton); - this.allButtons.Sort(static (a, b) => b.Priority - a.Priority); - this.DrawTitleBarButtons(); + var window = ImGuiP.GetCurrentWindow(); + + ImRect outRect; + ImGuiP.TitleBarRect(&outRect, window); + + var additionsButton = new TitleBarButton + { + Icon = FontAwesomeIcon.Bars, + IconOffset = new Vector2(2.5f, 1), + Click = _ => + { + this.internalIsClickthrough = false; + this.presetDirty = false; + ImGui.OpenPopup(additionsPopupName); + }, + Priority = int.MinValue, + AvailableClickthrough = true, + }; + + if (flagsApplicableForTitleBarIcons) + { + this.DrawTitleBarButtons(window, flags, outRect, + showAdditions + ? this.TitleBarButtons.Append(additionsButton) + : this.TitleBarButtons); + } } - if (wasFocused && this is not StyleEditorWindow) + if (wasFocused) { ImGui.PopStyleColor(); } @@ -675,14 +660,7 @@ public abstract class Window Task.FromResult<IDalamudTextureWrap>(tex)); } - if (isErrorStylePushed) - { - Style.StyleModelV1.DalamudStandard.Pop(); - } - else - { - this.PostDraw(); - } + this.PostDraw(); this.PostHandlePreset(persistence); @@ -778,11 +756,8 @@ public abstract class Window } } - private unsafe void DrawTitleBarButtons() + private unsafe void DrawTitleBarButtons(ImGuiWindowPtr window, ImGuiWindowFlags flags, ImRect titleBarRect, IEnumerable<TitleBarButton> buttons) { - var window = ImGuiP.GetCurrentWindow(); - var flags = window.Flags; - var titleBarRect = window.TitleBarRect(); ImGui.PushClipRect(ImGui.GetWindowPos(), ImGui.GetWindowPos() + ImGui.GetWindowSize(), false); var style = ImGui.GetStyle(); @@ -817,22 +792,26 @@ public abstract class Window var max = pos + new Vector2(fontSize, fontSize); ImRect bb = new(pos, max); var isClipped = !ImGuiP.ItemAdd(bb, id, null, 0); - bool hovered, held, pressed; + bool hovered, held; + var pressed = false; if (this.internalIsClickthrough) { + hovered = false; + held = false; + // ButtonBehavior does not function if the window is clickthrough, so we have to do it ourselves - var pad = ImGui.GetStyle().TouchExtraPadding; - var rect = new ImRect(pos - pad, max + pad); - hovered = rect.Contains(ImGui.GetMousePos()); + if (ImGui.IsMouseHoveringRect(pos, max)) + { + hovered = true; - // Temporarily enable inputs - // This will be reset on next frame, and then enabled again if it is still being hovered - if (hovered && ImGui.GetWindowViewport().ID != ImGui.GetMainViewport().ID) - ImGui.GetWindowViewport().Flags &= ~ImGuiViewportFlags.NoInputs; - - // We can't use ImGui native functions here, because they don't work with clickthrough - pressed = held = hovered && (GetKeyState(VK.VK_LBUTTON) & 0x8000) != 0; + // We can't use ImGui native functions here, because they don't work with clickthrough + if ((Windows.Win32.PInvoke.GetKeyState((int)VirtualKey.LBUTTON) & 0x8000) != 0) + { + held = true; + pressed = true; + } + } } else { @@ -861,10 +840,10 @@ public abstract class Window return pressed; } - foreach (var button in this.allButtons) + foreach (var button in buttons.OrderBy(x => x.Priority)) { if (this.internalIsClickthrough && !button.AvailableClickthrough) - continue; + return; Vector2 position = new(titleBarRect.Max.X - padR - buttonSize, titleBarRect.Min.Y + style.FramePadding.Y); padR += buttonSize + style.ItemInnerSpacing.X; @@ -908,7 +887,7 @@ public abstract class Window private void DrawErrorMessage() { // TODO: Once window systems are services, offer to reload the plugin - ImGui.TextColoredWrapped(ImGuiColors.DalamudRed, Loc.Localize("WindowSystemErrorOccurred", "An error occurred while rendering this window. Please contact the developer for details.")); + ImGui.TextColoredWrapped(ImGuiColors.DalamudRed,Loc.Localize("WindowSystemErrorOccurred", "An error occurred while rendering this window. Please contact the developer for details.")); ImGuiHelpers.ScaledDummy(5); @@ -1015,7 +994,7 @@ public abstract class Window /// <summary> /// Gets or sets an action that is called when the button is clicked. /// </summary> - public Action<ImGuiMouseButton>? Click { get; set; } + public Action<ImGuiMouseButton> Click { get; set; } /// <summary> /// Gets or sets the priority the button shall be shown in. diff --git a/Dalamud/Interface/Windowing/WindowSystem.cs b/Dalamud/Interface/Windowing/WindowSystem.cs index 319640336..87bd199a1 100644 --- a/Dalamud/Interface/Windowing/WindowSystem.cs +++ b/Dalamud/Interface/Windowing/WindowSystem.cs @@ -4,7 +4,6 @@ using System.Linq; using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Interface.Windowing.Persistence; - using Serilog; namespace Dalamud.Interface.Windowing; @@ -16,7 +15,7 @@ public class WindowSystem { private static DateTimeOffset lastAnyFocus; - private readonly List<Window> windows = []; + private readonly List<Window> windows = new(); private string lastFocusedWindowName = string.Empty; @@ -61,12 +60,6 @@ public class WindowSystem /// </summary> public string? Namespace { get; set; } - /// <summary> - /// Gets or sets a value indicating whether ATK close events should be inhibited while any window has focus. - /// Does not respect windows that are pinned or clickthrough. - /// </summary> - internal static bool ShouldInhibitAtkCloseEvents { get; set; } - /// <summary> /// Add a window to this <see cref="WindowSystem"/>. /// The window system doesn't own your window, it just renders it @@ -137,7 +130,7 @@ public class WindowSystem window.DrawInternal(flags, persistence); } - var focusedWindow = this.windows.FirstOrDefault(window => window.IsFocused); + var focusedWindow = this.windows.FirstOrDefault(window => window.IsFocused && window.RespectCloseHotkey); this.HasAnyFocus = focusedWindow != default; if (this.HasAnyFocus) @@ -162,11 +155,6 @@ public class WindowSystem } } - ShouldInhibitAtkCloseEvents |= this.windows.Any(w => w.IsFocused && - w.RespectCloseHotkey && - !w.IsPinned && - !w.IsClickthrough); - if (hasNamespace) ImGui.PopID(); } diff --git a/Dalamud/IoC/Internal/ObjectInstance.cs b/Dalamud/IoC/Internal/ObjectInstance.cs index af97b7124..3a963f6bd 100644 --- a/Dalamud/IoC/Internal/ObjectInstance.cs +++ b/Dalamud/IoC/Internal/ObjectInstance.cs @@ -1,3 +1,4 @@ +using System.Reflection; using System.Threading.Tasks; namespace Dalamud.IoC.Internal; diff --git a/Dalamud/IoC/Internal/ServiceContainer.cs b/Dalamud/IoC/Internal/ServiceContainer.cs index 0dacacc54..6383b6b11 100644 --- a/Dalamud/IoC/Internal/ServiceContainer.cs +++ b/Dalamud/IoC/Internal/ServiceContainer.cs @@ -20,10 +20,10 @@ namespace Dalamud.IoC.Internal; [ServiceManager.ProvidedService] internal class ServiceContainer : IServiceType { - private static readonly ModuleLog Log = ModuleLog.Create<ServiceContainer>(); + private static readonly ModuleLog Log = new("SERVICECONTAINER"); - private readonly Dictionary<Type, ObjectInstance> instances = []; - private readonly Dictionary<Type, Type> interfaceToTypeMap = []; + private readonly Dictionary<Type, ObjectInstance> instances = new(); + private readonly Dictionary<Type, Type> interfaceToTypeMap = new(); /// <summary> /// Initializes a new instance of the <see cref="ServiceContainer"/> class. diff --git a/Dalamud/Localization.cs b/Dalamud/Localization.cs index 8c7368c3f..0a7086e73 100644 --- a/Dalamud/Localization.cs +++ b/Dalamud/Localization.cs @@ -18,7 +18,7 @@ public class Localization : IServiceType /// <summary> /// Array of language codes which have a valid translation in Dalamud. /// </summary> - public static readonly string[] ApplicableLangCodes = ["de", "ja", "fr", "it", "es", "ko", "no", "ru", "zh", "tw"]; + public static readonly string[] ApplicableLangCodes = { "de", "ja", "fr", "it", "es", "ko", "no", "ru", "zh", "tw" }; private const string FallbackLangCode = "en"; diff --git a/Dalamud/Logging/Internal/TaskTracker.cs b/Dalamud/Logging/Internal/TaskTracker.cs index c6bd895a0..cb9a0db6d 100644 --- a/Dalamud/Logging/Internal/TaskTracker.cs +++ b/Dalamud/Logging/Internal/TaskTracker.cs @@ -15,8 +15,8 @@ namespace Dalamud.Logging.Internal; [ServiceManager.EarlyLoadedService] internal class TaskTracker : IInternalDisposableService { - private static readonly ModuleLog Log = ModuleLog.Create<TaskTracker>(); - private static readonly List<TaskInfo> TrackedTasksInternal = []; + private static readonly ModuleLog Log = new("TT"); + private static readonly List<TaskInfo> TrackedTasksInternal = new(); private static readonly ConcurrentQueue<TaskInfo> NewlyCreatedTasks = new(); private static bool clearRequested = false; diff --git a/Dalamud/Memory/MemoryHelper.cs b/Dalamud/Memory/MemoryHelper.cs index 5f7f69f2f..2eae1be6d 100644 --- a/Dalamud/Memory/MemoryHelper.cs +++ b/Dalamud/Memory/MemoryHelper.cs @@ -6,15 +6,11 @@ using System.Text; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Memory.Exceptions; using Dalamud.Utility; - using FFXIVClientStructs.FFXIV.Client.System.Memory; using FFXIVClientStructs.FFXIV.Client.System.String; - using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; - using Microsoft.Extensions.ObjectPool; - using Windows.Win32.Foundation; using Windows.Win32.System.Memory; @@ -268,31 +264,6 @@ public static unsafe class MemoryHelper } } - /// <summary> - /// Compares a UTF-16 character span with a null-terminated UTF-16 string at <paramref name="memoryAddress"/>. - /// </summary> - /// <param name="charSpan">UTF-16 character span (e.g., from a string literal).</param> - /// <param name="memoryAddress">Address of null-terminated UTF-16 (wide) string, as used by Windows APIs.</param> - /// <returns><see langword="true"/> if equal; otherwise, <see langword="false"/>.</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe bool EqualsZeroTerminatedWideString( - scoped ReadOnlySpan<char> charSpan, - nint memoryAddress) - { - if (memoryAddress == 0) - return charSpan.Length == 0; - - char* p = (char*)memoryAddress; - - foreach (char c in charSpan) - { - if (*p++ != c) - return false; - } - - return *p == '\0'; - } - /// <summary> /// Read a UTF-8 encoded string from a specified memory address. /// </summary> diff --git a/Dalamud/NativeMethods.json b/Dalamud/NativeMethods.json index 46fd3504f..ffb313dfc 100644 --- a/Dalamud/NativeMethods.json +++ b/Dalamud/NativeMethods.json @@ -1,5 +1,4 @@ { "$schema": "https://aka.ms/CsWin32.schema.json", - "useSafeHandles": false, "allowMarshaling": false } diff --git a/Dalamud/Networking/Http/HappyEyeballsCallback.cs b/Dalamud/Networking/Http/HappyEyeballsCallback.cs index 59bdff630..4e3ee61f6 100644 --- a/Dalamud/Networking/Http/HappyEyeballsCallback.cs +++ b/Dalamud/Networking/Http/HappyEyeballsCallback.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; @@ -20,7 +20,7 @@ namespace Dalamud.Networking.Http; /// </summary> public class HappyEyeballsCallback : IDisposable { - private static readonly ModuleLog Log = ModuleLog.Create<HappyEyeballsCallback>(); + private static readonly ModuleLog Log = new("HTTP"); /* * ToDo: Eventually add in some kind of state management to cache DNS and IP Family. diff --git a/Dalamud/Networking/Http/HappyHttpClient.cs b/Dalamud/Networking/Http/HappyHttpClient.cs index c6a476fff..aeed98695 100644 --- a/Dalamud/Networking/Http/HappyHttpClient.cs +++ b/Dalamud/Networking/Http/HappyHttpClient.cs @@ -36,7 +36,7 @@ internal class HappyHttpClient : IInternalDisposableService { UserAgent = { - new ProductInfoHeaderValue("Dalamud", Versioning.GetAssemblyVersion()), + new ProductInfoHeaderValue("Dalamud", Util.AssemblyVersion), }, }, }; diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs index 48f91b250..6fd9064b6 100644 --- a/Dalamud/Plugin/DalamudPluginInterface.cs +++ b/Dalamud/Plugin/DalamudPluginInterface.cs @@ -16,15 +16,18 @@ using Dalamud.Game.Text; using Dalamud.Game.Text.Sanitizer; using Dalamud.Interface; using Dalamud.Interface.Internal; +using Dalamud.Interface.Internal.Windows.PluginInstaller; +using Dalamud.Interface.Internal.Windows.SelfTest; +using Dalamud.Interface.Internal.Windows.Settings; using Dalamud.IoC.Internal; using Dalamud.Plugin.Internal; using Dalamud.Plugin.Internal.AutoUpdate; using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Internal.Types.Manifest; using Dalamud.Plugin.Ipc; +using Dalamud.Plugin.Ipc.Exceptions; using Dalamud.Plugin.Ipc.Internal; -using Dalamud.Plugin.VersionInfo; -using Dalamud.Utility; +using Dalamud.Plugin.Services; using Serilog; @@ -201,7 +204,11 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa return true; } - /// <inheritdoc/> + /// <summary> + /// Gets the plugin the given assembly is part of. + /// </summary> + /// <param name="assembly">The assembly to check.</param> + /// <returns>The plugin the given assembly is part of, or null if this is a shared assembly or if this information cannot be determined.</returns> public IExposedPlugin? GetPlugin(Assembly assembly) => AssemblyLoadContext.GetLoadContext(assembly) switch { @@ -209,7 +216,11 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa var context => this.GetPlugin(context), }; - /// <inheritdoc/> + /// <summary> + /// Gets the plugin that loads in the given context. + /// </summary> + /// <param name="context">The context to check.</param> + /// <returns>The plugin that loads in the given context, or null if this isn't a plugin's context or if this information cannot be determined.</returns> public IExposedPlugin? GetPlugin(AssemblyLoadContext context) => Service<PluginManager>.Get().InstalledPlugins.FirstOrDefault(p => p.LoadsIn(context)) switch { @@ -217,101 +228,95 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa var p => new ExposedPlugin(p), }; - /// <inheritdoc/> - public IDalamudVersionInfo GetDalamudVersion() - { - return new DalamudVersionInfo(Versioning.GetAssemblyVersionParsed(), Versioning.GetActiveTrack(), Versioning.GetGitHash(), Versioning.GetGitHashClientStructs(), Versioning.GetScmVersion()); - } - #region IPC /// <inheritdoc/> public T GetOrCreateData<T>(string tag, Func<T> dataGenerator) where T : class - => Service<DataShare>.Get().GetOrCreateData(tag, new DataCachePluginId(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId), dataGenerator); + => Service<DataShare>.Get().GetOrCreateData(tag, dataGenerator); /// <inheritdoc/> public void RelinquishData(string tag) - => Service<DataShare>.Get().RelinquishData(tag, new DataCachePluginId(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId)); + => Service<DataShare>.Get().RelinquishData(tag); /// <inheritdoc/> public bool TryGetData<T>(string tag, [NotNullWhen(true)] out T? data) where T : class - => Service<DataShare>.Get().TryGetData(tag, new DataCachePluginId(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId), out data); + => Service<DataShare>.Get().TryGetData(tag, out data); /// <inheritdoc/> public T? GetData<T>(string tag) where T : class - => Service<DataShare>.Get().GetData<T>(tag, new DataCachePluginId(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId)); + => Service<DataShare>.Get().GetData<T>(tag); /// <inheritdoc/> public ICallGateProvider<TRet> GetIpcProvider<TRet>(string name) - => new CallGatePubSub<TRet>(name, this.plugin); + => new CallGatePubSub<TRet>(name); /// <inheritdoc/> public ICallGateProvider<T1, TRet> GetIpcProvider<T1, TRet>(string name) - => new CallGatePubSub<T1, TRet>(name, this.plugin); + => new CallGatePubSub<T1, TRet>(name); /// <inheritdoc/> public ICallGateProvider<T1, T2, TRet> GetIpcProvider<T1, T2, TRet>(string name) - => new CallGatePubSub<T1, T2, TRet>(name, this.plugin); + => new CallGatePubSub<T1, T2, TRet>(name); /// <inheritdoc/> public ICallGateProvider<T1, T2, T3, TRet> GetIpcProvider<T1, T2, T3, TRet>(string name) - => new CallGatePubSub<T1, T2, T3, TRet>(name, this.plugin); + => new CallGatePubSub<T1, T2, T3, TRet>(name); /// <inheritdoc/> public ICallGateProvider<T1, T2, T3, T4, TRet> GetIpcProvider<T1, T2, T3, T4, TRet>(string name) - => new CallGatePubSub<T1, T2, T3, T4, TRet>(name, this.plugin); + => new CallGatePubSub<T1, T2, T3, T4, TRet>(name); /// <inheritdoc/> public ICallGateProvider<T1, T2, T3, T4, T5, TRet> GetIpcProvider<T1, T2, T3, T4, T5, TRet>(string name) - => new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name, this.plugin); + => new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name); /// <inheritdoc/> public ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, TRet>(string name) - => new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name, this.plugin); + => new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name); /// <inheritdoc/> public ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, T7, TRet>(string name) - => new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name, this.plugin); + => new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name); /// <inheritdoc/> public ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(string name) - => new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name, this.plugin); + => new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name); /// <inheritdoc/> public ICallGateSubscriber<TRet> GetIpcSubscriber<TRet>(string name) - => new CallGatePubSub<TRet>(name, this.plugin); + => new CallGatePubSub<TRet>(name); /// <inheritdoc/> public ICallGateSubscriber<T1, TRet> GetIpcSubscriber<T1, TRet>(string name) - => new CallGatePubSub<T1, TRet>(name, this.plugin); + => new CallGatePubSub<T1, TRet>(name); /// <inheritdoc/> public ICallGateSubscriber<T1, T2, TRet> GetIpcSubscriber<T1, T2, TRet>(string name) - => new CallGatePubSub<T1, T2, TRet>(name, this.plugin); + => new CallGatePubSub<T1, T2, TRet>(name); /// <inheritdoc/> public ICallGateSubscriber<T1, T2, T3, TRet> GetIpcSubscriber<T1, T2, T3, TRet>(string name) - => new CallGatePubSub<T1, T2, T3, TRet>(name, this.plugin); + => new CallGatePubSub<T1, T2, T3, TRet>(name); /// <inheritdoc/> public ICallGateSubscriber<T1, T2, T3, T4, TRet> GetIpcSubscriber<T1, T2, T3, T4, TRet>(string name) - => new CallGatePubSub<T1, T2, T3, T4, TRet>(name, this.plugin); + => new CallGatePubSub<T1, T2, T3, T4, TRet>(name); /// <inheritdoc/> public ICallGateSubscriber<T1, T2, T3, T4, T5, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, TRet>(string name) - => new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name, this.plugin); + => new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name); /// <inheritdoc/> public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, TRet>(string name) - => new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name, this.plugin); + => new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name); /// <inheritdoc/> public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet>(string name) - => new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name, this.plugin); + => new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name); /// <inheritdoc/> public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(string name) - => new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name, this.plugin); + => new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name); #endregion @@ -342,7 +347,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa { var mi = this.configs.GetType().GetMethod("LoadForType"); var fn = mi.MakeGenericMethod(type); - return (IPluginConfiguration)fn.Invoke(this.configs, [this.plugin.InternalName]); + return (IPluginConfiguration)fn.Invoke(this.configs, new object[] { this.plugin.InternalName }); } } diff --git a/Dalamud/Plugin/IDalamudPluginInterface.cs b/Dalamud/Plugin/IDalamudPluginInterface.cs index 92ecab006..d1b6977d4 100644 --- a/Dalamud/Plugin/IDalamudPluginInterface.cs +++ b/Dalamud/Plugin/IDalamudPluginInterface.cs @@ -15,7 +15,7 @@ using Dalamud.Plugin.Internal.Types.Manifest; using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Ipc.Exceptions; using Dalamud.Plugin.Ipc.Internal; -using Dalamud.Plugin.VersionInfo; +using Dalamud.Plugin.Services; namespace Dalamud.Plugin; @@ -194,12 +194,6 @@ public interface IDalamudPluginInterface : IServiceProvider /// <returns>The plugin that loads in the given context, or null if this isn't a plugin's context or if this information cannot be determined.</returns> IExposedPlugin? GetPlugin(AssemblyLoadContext context); - /// <summary> - /// Gets information about the version of Dalamud this plugin is loaded into. - /// </summary> - /// <returns>Class containing version information.</returns> - IDalamudVersionInfo GetDalamudVersion(); - /// <inheritdoc cref="DataShare.GetOrCreateData{T}"/> T GetOrCreateData<T>(string tag, Func<T> dataGenerator) where T : class; diff --git a/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs b/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs index 734bc5ef9..3fc011a68 100644 --- a/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs +++ b/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Threading.Tasks; using CheapLoc; - using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Console; @@ -33,7 +32,7 @@ namespace Dalamud.Plugin.Internal.AutoUpdate; [ServiceManager.EarlyLoadedService] internal class AutoUpdateManager : IServiceType { - private static readonly ModuleLog Log = ModuleLog.Create<AutoUpdateManager>(); + private static readonly ModuleLog Log = new("AUTOUPDATE"); /// <summary> /// Time we should wait after login to update. @@ -377,7 +376,7 @@ internal class AutoUpdateManager : IServiceType } } - private void NotifyUpdatesAreAvailable(List<AvailablePluginUpdate> updatablePlugins) + private void NotifyUpdatesAreAvailable(ICollection<AvailablePluginUpdate> updatablePlugins) { if (updatablePlugins.Count == 0) return; diff --git a/Dalamud/Plugin/Internal/Loader/AssemblyLoadContextBuilder.cs b/Dalamud/Plugin/Internal/Loader/AssemblyLoadContextBuilder.cs index aa304cd05..1c6e6feed 100644 --- a/Dalamud/Plugin/Internal/Loader/AssemblyLoadContextBuilder.cs +++ b/Dalamud/Plugin/Internal/Loader/AssemblyLoadContextBuilder.cs @@ -15,9 +15,9 @@ namespace Dalamud.Plugin.Internal.Loader; /// </summary> internal class AssemblyLoadContextBuilder { - private readonly List<string> additionalProbingPaths = []; - private readonly List<string> resourceProbingPaths = []; - private readonly List<string> resourceProbingSubpaths = []; + private readonly List<string> additionalProbingPaths = new(); + private readonly List<string> resourceProbingPaths = new(); + private readonly List<string> resourceProbingSubpaths = new(); private readonly Dictionary<string, ManagedLibrary> managedLibraries = new(StringComparer.Ordinal); private readonly Dictionary<string, NativeLibrary> nativeLibraries = new(StringComparer.Ordinal); private readonly HashSet<string> privateAssemblies = new(StringComparer.Ordinal); @@ -140,7 +140,7 @@ internal class AssemblyLoadContextBuilder return this; } - var names = new Queue<AssemblyName>([assemblyName]); + var names = new Queue<AssemblyName>(new[] { assemblyName }); while (names.TryDequeue(out var name)) { diff --git a/Dalamud/Plugin/Internal/Loader/LoaderConfig.cs b/Dalamud/Plugin/Internal/Loader/LoaderConfig.cs index e26338820..b863b8ee1 100644 --- a/Dalamud/Plugin/Internal/Loader/LoaderConfig.cs +++ b/Dalamud/Plugin/Internal/Loader/LoaderConfig.cs @@ -39,13 +39,13 @@ internal class LoaderConfig /// <summary> /// Gets a list of assemblies which should be treated as private. /// </summary> - public ICollection<AssemblyName> PrivateAssemblies { get; } = []; + public ICollection<AssemblyName> PrivateAssemblies { get; } = new List<AssemblyName>(); /// <summary> /// Gets a list of assemblies which should be unified between the host and the plugin. /// </summary> /// <seealso href="https://github.com/natemcmaster/DotNetCorePlugins/blob/main/docs/what-are-shared-types.md">what-are-shared-types</seealso> - public ICollection<(AssemblyName Name, bool Recursive)> SharedAssemblies { get; } = []; + public ICollection<(AssemblyName Name, bool Recursive)> SharedAssemblies { get; } = new List<(AssemblyName Name, bool Recursive)>(); /// <summary> /// Gets or sets a value indicating whether attempt to unify all types from a plugin with the host. diff --git a/Dalamud/Plugin/Internal/Loader/ManagedLoadContext.cs b/Dalamud/Plugin/Internal/Loader/ManagedLoadContext.cs index a85d20e40..4ea4eb5c4 100644 --- a/Dalamud/Plugin/Internal/Loader/ManagedLoadContext.cs +++ b/Dalamud/Plugin/Internal/Loader/ManagedLoadContext.cs @@ -24,7 +24,7 @@ internal class ManagedLoadContext : AssemblyLoadContext private readonly IReadOnlyDictionary<string, ManagedLibrary> managedAssemblies; private readonly IReadOnlyDictionary<string, NativeLibrary> nativeLibraries; private readonly IReadOnlyCollection<string> privateAssemblies; - private readonly List<string> defaultAssemblies; + private readonly ICollection<string> defaultAssemblies; private readonly IReadOnlyCollection<string> additionalProbingPaths; private readonly bool preferDefaultLoadContext; private readonly string[] resourceRoots; @@ -64,7 +64,8 @@ internal class ManagedLoadContext : AssemblyLoadContext bool shadowCopyNativeLibraries) : base(Path.GetFileNameWithoutExtension(mainAssemblyPath), isCollectible) { - ArgumentNullException.ThrowIfNull(resourceProbingPaths); + if (resourceProbingPaths == null) + throw new ArgumentNullException(nameof(resourceProbingPaths)); this.mainAssemblyPath = mainAssemblyPath ?? throw new ArgumentNullException(nameof(mainAssemblyPath)); this.dependencyResolver = new AssemblyDependencyResolver(mainAssemblyPath); @@ -242,7 +243,7 @@ internal class ManagedLoadContext : AssemblyLoadContext } // check to see if there is a library entry for the library without the file extension - var trimmedName = unmanagedDllName[..^suffix.Length]; + var trimmedName = unmanagedDllName.Substring(0, unmanagedDllName.Length - suffix.Length); if (this.nativeLibraries.TryGetValue(prefix + trimmedName, out library)) { diff --git a/Dalamud/Plugin/Internal/Loader/PlatformInformation.cs b/Dalamud/Plugin/Internal/Loader/PlatformInformation.cs index 151d184db..ec1d557be 100644 --- a/Dalamud/Plugin/Internal/Loader/PlatformInformation.cs +++ b/Dalamud/Plugin/Internal/Loader/PlatformInformation.cs @@ -11,21 +11,21 @@ internal class PlatformInformation /// <summary> /// Gets a list of native OS specific library extensions. /// </summary> - public static string[] NativeLibraryExtensions => [".dll"]; + public static string[] NativeLibraryExtensions => new[] { ".dll" }; /// <summary> /// Gets a list of native OS specific library prefixes. /// </summary> - public static string[] NativeLibraryPrefixes => [string.Empty]; + public static string[] NativeLibraryPrefixes => new[] { string.Empty }; /// <summary> /// Gets a list of native OS specific managed assembly extensions. /// </summary> - public static string[] ManagedAssemblyExtensions => - [ + public static string[] ManagedAssemblyExtensions => new[] + { ".dll", ".ni.dll", ".exe", ".ni.exe", - ]; + }; } diff --git a/Dalamud/Plugin/Internal/Loader/PluginLoader.cs b/Dalamud/Plugin/Internal/Loader/PluginLoader.cs index a77bfe088..54b9cad4b 100644 --- a/Dalamud/Plugin/Internal/Loader/PluginLoader.cs +++ b/Dalamud/Plugin/Internal/Loader/PluginLoader.cs @@ -53,7 +53,8 @@ internal class PluginLoader : IDisposable /// <returns>A loader.</returns> public static PluginLoader CreateFromAssemblyFile(string assemblyFile, Action<LoaderConfig> configure) { - ArgumentNullException.ThrowIfNull(configure); + if (configure == null) + throw new ArgumentNullException(nameof(configure)); var config = new LoaderConfig(assemblyFile); configure(config); @@ -158,6 +159,7 @@ internal class PluginLoader : IDisposable private void EnsureNotDisposed() { - ObjectDisposedException.ThrowIf(this.disposed, this); + if (this.disposed) + throw new ObjectDisposedException(nameof(PluginLoader)); } } diff --git a/Dalamud/Plugin/Internal/PluginErrorHandler.cs b/Dalamud/Plugin/Internal/PluginErrorHandler.cs index 6733cc3ca..0094c3751 100644 --- a/Dalamud/Plugin/Internal/PluginErrorHandler.cs +++ b/Dalamud/Plugin/Internal/PluginErrorHandler.cs @@ -22,7 +22,7 @@ internal class PluginErrorHandler : IServiceType private readonly NotificationManager notificationManager; private readonly DalamudInterface di; - private readonly Dictionary<Type, Delegate> invokerCache = []; + private readonly Dictionary<Type, Delegate> invokerCache = new(); private DateTime lastErrorTime = DateTime.MinValue; private IActiveNotification? activeNotification; @@ -141,7 +141,10 @@ internal class PluginErrorHandler : IServiceType private static Action<TDelegate, object[]> CreateInvoker<TDelegate>() where TDelegate : Delegate { var delegateType = typeof(TDelegate); - var method = delegateType.GetMethod("Invoke") ?? throw new InvalidOperationException($"Delegate {delegateType} does not have an Invoke method."); + var method = delegateType.GetMethod("Invoke"); + if (method == null) + throw new InvalidOperationException($"Delegate {delegateType} does not have an Invoke method."); + var parameters = method.GetParameters(); // Create parameters for the lambda @@ -150,7 +153,7 @@ internal class PluginErrorHandler : IServiceType // Create expressions to convert array elements to parameter types var callArgs = new Expression[parameters.Length]; - for (var i = 0; i < parameters.Length; i++) + for (int i = 0; i < parameters.Length; i++) { var paramType = parameters[i].ParameterType; var arrayAccess = Expression.ArrayIndex(argsParam, Expression.Constant(i)); diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs index 0c9894380..e2eded57c 100644 --- a/Dalamud/Plugin/Internal/PluginManager.cs +++ b/Dalamud/Plugin/Internal/PluginManager.cs @@ -11,7 +11,6 @@ using System.Threading; using System.Threading.Tasks; using CheapLoc; - using Dalamud.Configuration; using Dalamud.Configuration.Internal; using Dalamud.Game; @@ -32,7 +31,6 @@ using Dalamud.Plugin.Ipc.Internal; using Dalamud.Support; using Dalamud.Utility; using Dalamud.Utility.Timing; - using Newtonsoft.Json; namespace Dalamud.Plugin.Internal; @@ -58,9 +56,9 @@ internal class PluginManager : IInternalDisposableService private readonly DirectoryInfo pluginDirectory; private readonly BannedPlugin[]? bannedPlugins; - private readonly List<LocalPlugin> installedPluginsList = []; - private readonly List<RemotePluginManifest> availablePluginsList = []; - private readonly List<AvailablePluginUpdate> updatablePluginsList = []; + private readonly List<LocalPlugin> installedPluginsList = new(); + private readonly List<RemotePluginManifest> availablePluginsList = new(); + private readonly List<AvailablePluginUpdate> updatablePluginsList = new(); private readonly Task<DalamudLinkPayload> openInstallerWindowPluginChangelogsLink; @@ -133,7 +131,7 @@ internal class PluginManager : IInternalDisposableService PluginInstallerOpenKind.Changelogs); })); - this.configuration.PluginTestingOptIns ??= []; + this.configuration.PluginTestingOptIns ??= new(); this.MainRepo = PluginRepository.CreateMainRepo(this.happyHttpClient); registerStartupBlocker( @@ -232,7 +230,7 @@ internal class PluginManager : IInternalDisposableService /// <summary> /// Gets a list of all plugin repositories. The main repo should always be first. /// </summary> - public List<PluginRepository> Repos { get; private set; } = []; + public List<PluginRepository> Repos { get; private set; } = new(); /// <summary> /// Gets a value indicating whether plugins are not still loading from boot. @@ -1516,7 +1514,11 @@ internal class PluginManager : IInternalDisposableService JsonConvert.SerializeObject(repoManifest, Formatting.Indented)); // Reload as a local manifest, add some attributes, and save again. - var tempManifest = LocalPluginManifest.Load(tempManifestFile) ?? throw new Exception("Plugin had no valid manifest"); + var tempManifest = LocalPluginManifest.Load(tempManifestFile); + + if (tempManifest == null) + throw new Exception("Plugin had no valid manifest"); + if (tempManifest.InternalName != repoManifest.InternalName) { throw new Exception( @@ -1788,7 +1790,7 @@ internal class PluginManager : IInternalDisposableService var updates = this.AvailablePlugins .Where(remoteManifest => plugin.Manifest.InternalName == remoteManifest.InternalName) .Where(remoteManifest => plugin.Manifest.InstalledFromUrl == remoteManifest.SourceRepo.PluginMasterUrl || !remoteManifest.SourceRepo.IsThirdParty) - .Where(remoteManifest => remoteManifest.MinimumDalamudVersion == null || Versioning.GetAssemblyVersionParsed() >= remoteManifest.MinimumDalamudVersion) + .Where(remoteManifest => remoteManifest.MinimumDalamudVersion == null || Util.AssemblyVersionParsed >= remoteManifest.MinimumDalamudVersion) .Where(remoteManifest => { var useTesting = this.UseTesting(remoteManifest); @@ -1895,9 +1897,9 @@ internal class PluginManager : IInternalDisposableService /// </summary> public class StartupLoadTracker { - private readonly Dictionary<string, string> internalToPublic = []; - private readonly ConcurrentBag<string> allInternalNames = []; - private readonly ConcurrentBag<string> finishedInternalNames = []; + private readonly Dictionary<string, string> internalToPublic = new(); + private readonly ConcurrentBag<string> allInternalNames = new(); + private readonly ConcurrentBag<string> finishedInternalNames = new(); /// <summary> /// Gets a value indicating the total load progress. diff --git a/Dalamud/Plugin/Internal/PluginValidator.cs b/Dalamud/Plugin/Internal/PluginValidator.cs index 4c6bb9fef..b2cbe5520 100644 --- a/Dalamud/Plugin/Internal/PluginValidator.cs +++ b/Dalamud/Plugin/Internal/PluginValidator.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Dalamud.Game.Command; @@ -11,7 +11,7 @@ namespace Dalamud.Plugin.Internal; /// </summary> internal static class PluginValidator { - private static readonly char[] LineSeparator = [' ', '\n', '\r']; + private static readonly char[] LineSeparator = new[] { ' ', '\n', '\r' }; /// <summary> /// Represents the severity of a validation problem. diff --git a/Dalamud/Plugin/Internal/Profiles/PluginManagementCommandHandler.cs b/Dalamud/Plugin/Internal/Profiles/PluginManagementCommandHandler.cs index 6608f2669..09cceebcb 100644 --- a/Dalamud/Plugin/Internal/Profiles/PluginManagementCommandHandler.cs +++ b/Dalamud/Plugin/Internal/Profiles/PluginManagementCommandHandler.cs @@ -3,14 +3,12 @@ using System.Linq; using System.Threading.Tasks; using CheapLoc; - using Dalamud.Game; using Dalamud.Game.Command; using Dalamud.Game.Gui; using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Services; using Dalamud.Utility; - using Serilog; namespace Dalamud.Plugin.Internal.Profiles; @@ -41,7 +39,7 @@ internal class PluginManagementCommandHandler : IInternalDisposableService private readonly ChatGui chat; private readonly Framework framework; - private List<(Target Target, PluginCommandOperation Operation)> commandQueue = []; + private List<(Target Target, PluginCommandOperation Operation)> commandQueue = new(); /// <summary> /// Initializes a new instance of the <see cref="PluginManagementCommandHandler"/> class. diff --git a/Dalamud/Plugin/Internal/Profiles/Profile.cs b/Dalamud/Plugin/Internal/Profiles/Profile.cs index 9d931d9ed..d899b0cca 100644 --- a/Dalamud/Plugin/Internal/Profiles/Profile.cs +++ b/Dalamud/Plugin/Internal/Profiles/Profile.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; @@ -14,7 +14,7 @@ namespace Dalamud.Plugin.Internal.Profiles; /// </summary> internal class Profile { - private static readonly ModuleLog Log = ModuleLog.Create<Profile>(); + private static readonly ModuleLog Log = new("PROFILE"); private readonly ProfileManager manager; private readonly ProfileModelV1 modelV1; diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs index bbe678162..775ff7a72 100644 --- a/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs +++ b/Dalamud/Plugin/Internal/Profiles/ProfileManager.cs @@ -1,11 +1,10 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using CheapLoc; - using Dalamud.Configuration.Internal; using Dalamud.Logging.Internal; using Dalamud.Utility; @@ -16,12 +15,12 @@ namespace Dalamud.Plugin.Internal.Profiles; /// Class responsible for managing plugin profiles. /// </summary> [ServiceManager.BlockingEarlyLoadedService($"Data provider for {nameof(PluginManager)}.")] -internal partial class ProfileManager : IServiceType +internal class ProfileManager : IServiceType { - private static readonly ModuleLog Log = ModuleLog.Create<ProfileManager>(); + private static readonly ModuleLog Log = new("PROFMAN"); private readonly DalamudConfiguration config; - private readonly List<Profile> profiles = []; + private readonly List<Profile> profiles = new(); private volatile bool isBusy = false; @@ -152,7 +151,11 @@ internal partial class ProfileManager : IServiceType /// <returns>The newly cloned profile.</returns> public Profile CloneProfile(Profile toClone) { - return this.ImportProfile(toClone.Model.SerializeForShare()) ?? throw new Exception("New profile was null while cloning"); + var newProfile = this.ImportProfile(toClone.Model.SerializeForShare()); + if (newProfile == null) + throw new Exception("New profile was null while cloning"); + + return newProfile; } /// <summary> @@ -335,15 +338,12 @@ internal partial class ProfileManager : IServiceType } } - [GeneratedRegex(@" \(.* Mix\)")] - private static partial Regex MixRegex(); - private string GenerateUniqueProfileName(string startingWith) { if (this.profiles.All(x => x.Name != startingWith)) return startingWith; - startingWith = MixRegex().Replace(startingWith, string.Empty); + startingWith = Regex.Replace(startingWith, @" \(.* Mix\)", string.Empty); while (true) { @@ -359,7 +359,7 @@ internal partial class ProfileManager : IServiceType this.config.DefaultProfile ??= new ProfileModelV1(); this.profiles.Add(new Profile(this, this.config.DefaultProfile, true, true)); - this.config.SavedProfiles ??= []; + this.config.SavedProfiles ??= new List<ProfileModel>(); foreach (var profileModel in this.config.SavedProfiles) { this.profiles.Add(new Profile(this, profileModel, false, true)); diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs b/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs index 37487e321..e3d9e2955 100644 --- a/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs +++ b/Dalamud/Plugin/Internal/Profiles/ProfileModel.cs @@ -1,8 +1,7 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Reflection; using Dalamud.Utility; - using Newtonsoft.Json; using Newtonsoft.Json.Serialization; @@ -33,7 +32,7 @@ public abstract class ProfileModel /// <exception cref="ArgumentException">Thrown when the parsed string is not a valid profile.</exception> public static ProfileModel? Deserialize(string model) { - var json = Util.DecompressString(Convert.FromBase64String(model[3..])); + var json = Util.DecompressString(Convert.FromBase64String(model.Substring(3))); if (model.StartsWith(ProfileModelV1.SerializedPrefix)) return JsonConvert.DeserializeObject<ProfileModelV1>(json); @@ -48,15 +47,19 @@ public abstract class ProfileModel /// <exception cref="ArgumentOutOfRangeException">Thrown when an unsupported model is serialized.</exception> public string SerializeForShare() { - var prefix = this switch + string prefix; + switch (this) { - ProfileModelV1 => ProfileModelV1.SerializedPrefix, - _ => throw new ArgumentOutOfRangeException(), - }; + case ProfileModelV1: + prefix = ProfileModelV1.SerializedPrefix; + break; + default: + throw new ArgumentOutOfRangeException(); + } // HACK: Just filter the ID for now, we should split the sharing + saving model var serialized = JsonConvert.SerializeObject(this, new JsonSerializerSettings() - { ContractResolver = new IgnorePropertiesResolver(["WorkingPluginId"]) }); + { ContractResolver = new IgnorePropertiesResolver(new[] { "WorkingPluginId" }) }); return prefix + Convert.ToBase64String(Util.CompressString(serialized)); } diff --git a/Dalamud/Plugin/Internal/Profiles/ProfileModelV1.cs b/Dalamud/Plugin/Internal/Profiles/ProfileModelV1.cs index deee9c04f..a1a327c1d 100644 --- a/Dalamud/Plugin/Internal/Profiles/ProfileModelV1.cs +++ b/Dalamud/Plugin/Internal/Profiles/ProfileModelV1.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Newtonsoft.Json; @@ -63,7 +63,7 @@ public class ProfileModelV1 : ProfileModel /// <summary> /// Gets or sets the list of plugins in this profile. /// </summary> - public List<ProfileModelV1Plugin> Plugins { get; set; } = []; + public List<ProfileModelV1Plugin> Plugins { get; set; } = new(); /// <summary> /// Class representing a single plugin in a profile. diff --git a/Dalamud/Plugin/Internal/Types/LocalDevPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalDevPlugin.cs index 2e20d2aef..34b54163a 100644 --- a/Dalamud/Plugin/Internal/Types/LocalDevPlugin.cs +++ b/Dalamud/Plugin/Internal/Types/LocalDevPlugin.cs @@ -17,7 +17,7 @@ namespace Dalamud.Plugin.Internal.Types; /// </summary> internal sealed class LocalDevPlugin : LocalPlugin { - private static readonly ModuleLog Log = ModuleLog.Create<LocalDevPlugin>(); + private static readonly ModuleLog Log = new("PLUGIN"); // Ref to Dalamud.Configuration.DevPluginSettings private readonly DevPluginSettings devSettings; diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs index 29867b355..0197683ef 100644 --- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs +++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs @@ -33,7 +33,7 @@ internal class LocalPlugin : IAsyncDisposable protected LocalPluginManifest manifest; #pragma warning restore SA1401 - private static readonly ModuleLog Log = ModuleLog.Create<LocalPlugin>(); + private static readonly ModuleLog Log = new("LOCALPLUGIN"); private readonly FileInfo manifestFile; private readonly FileInfo disabledFile; @@ -315,7 +315,7 @@ internal class LocalPlugin : IAsyncDisposable if (!this.CheckPolicy()) throw new PluginPreconditionFailedException($"Unable to load {this.Name} as a load policy forbids it"); - if (this.Manifest.MinimumDalamudVersion != null && this.Manifest.MinimumDalamudVersion > Versioning.GetAssemblyVersionParsed()) + if (this.Manifest.MinimumDalamudVersion != null && this.Manifest.MinimumDalamudVersion > Util.AssemblyVersionParsed) throw new PluginPreconditionFailedException($"Unable to load {this.Name}, Dalamud version is lower than minimum required version {this.Manifest.MinimumDalamudVersion}"); this.State = PluginState.Loading; diff --git a/Dalamud/Plugin/Internal/Types/Manifest/LocalPluginManifest.cs b/Dalamud/Plugin/Internal/Types/Manifest/LocalPluginManifest.cs index 73d23d19f..3aededa18 100644 --- a/Dalamud/Plugin/Internal/Types/Manifest/LocalPluginManifest.cs +++ b/Dalamud/Plugin/Internal/Types/Manifest/LocalPluginManifest.cs @@ -1,9 +1,7 @@ using System.IO; using Dalamud.Utility; - using Newtonsoft.Json; - using Serilog; namespace Dalamud.Plugin.Internal.Types.Manifest; diff --git a/Dalamud/Plugin/Internal/Types/Manifest/RemotePluginManifest.cs b/Dalamud/Plugin/Internal/Types/Manifest/RemotePluginManifest.cs index b83ba91a8..47e92cd84 100644 --- a/Dalamud/Plugin/Internal/Types/Manifest/RemotePluginManifest.cs +++ b/Dalamud/Plugin/Internal/Types/Manifest/RemotePluginManifest.cs @@ -1,5 +1,4 @@ using JetBrains.Annotations; - using Newtonsoft.Json; namespace Dalamud.Plugin.Internal.Types.Manifest; diff --git a/Dalamud/Plugin/Internal/Types/PluginDef.cs b/Dalamud/Plugin/Internal/Types/PluginDef.cs index f9f49225a..25cd82423 100644 --- a/Dalamud/Plugin/Internal/Types/PluginDef.cs +++ b/Dalamud/Plugin/Internal/Types/PluginDef.cs @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Internal.Types; /// <summary> /// Plugin Definition. /// </summary> -internal readonly struct PluginDef +internal struct PluginDef { /// <summary> /// Initializes a new instance of the <see cref="PluginDef"/> struct. diff --git a/Dalamud/Plugin/Internal/Types/PluginManifest.cs b/Dalamud/Plugin/Internal/Types/PluginManifest.cs index fc9f4e372..cbf69bb5e 100644 --- a/Dalamud/Plugin/Internal/Types/PluginManifest.cs +++ b/Dalamud/Plugin/Internal/Types/PluginManifest.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using Dalamud.Common.Game; using Dalamud.Plugin.Internal.Types.Manifest; - using Newtonsoft.Json; namespace Dalamud.Plugin.Internal.Types; diff --git a/Dalamud/Plugin/Internal/Types/PluginRepository.cs b/Dalamud/Plugin/Internal/Types/PluginRepository.cs index d47a3727d..c5e703e4b 100644 --- a/Dalamud/Plugin/Internal/Types/PluginRepository.cs +++ b/Dalamud/Plugin/Internal/Types/PluginRepository.cs @@ -29,7 +29,7 @@ internal class PluginRepository private const int HttpRequestTimeoutSeconds = 20; - private static readonly ModuleLog Log = ModuleLog.Create<PluginRepository>(); + private static readonly ModuleLog Log = new("PLUGINR"); private readonly HttpClient httpClient; /// <summary> @@ -59,7 +59,7 @@ internal class PluginRepository }, UserAgent = { - new ProductInfoHeaderValue("Dalamud", Versioning.GetAssemblyVersion()), + new ProductInfoHeaderValue("Dalamud", Util.AssemblyVersion), }, }, }; @@ -119,7 +119,13 @@ internal class PluginRepository response.EnsureSuccessStatusCode(); var data = await response.Content.ReadAsStringAsync(); - var pluginMaster = JsonConvert.DeserializeObject<List<RemotePluginManifest>>(data) ?? throw new Exception("Deserialized PluginMaster was null."); + var pluginMaster = JsonConvert.DeserializeObject<List<RemotePluginManifest>>(data); + + if (pluginMaster == null) + { + throw new Exception("Deserialized PluginMaster was null."); + } + pluginMaster.Sort((pm1, pm2) => string.Compare(pm1.Name, pm2.Name, StringComparison.Ordinal)); // Set the source for each remote manifest. Allows for checking if is 3rd party. @@ -158,7 +164,7 @@ internal class PluginRepository } this.PluginMaster = pluginMaster.Where(this.IsValidManifest).ToList().AsReadOnly(); - + // API9 HACK: Force IsHide to false, we should remove that if (!this.IsThirdParty) { @@ -191,7 +197,7 @@ internal class PluginRepository Log.Error("Plugin {PluginName} in {RepoLink} has an invalid Name.", manifest.InternalName, this.PluginMasterUrl); return false; } - + // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (manifest.AssemblyVersion == null) { @@ -218,7 +224,7 @@ internal class PluginRepository request.Headers.CacheControl = new CacheControlHeaderValue { NoCache = true }; using var requestCts = new CancellationTokenSource(TimeSpan.FromSeconds(timeout)); - + return await httpClient.SendAsync(request, requestCts.Token); } } diff --git a/Dalamud/Plugin/Ipc/Exceptions/DataCacheCreationError.cs b/Dalamud/Plugin/Ipc/Exceptions/DataCacheCreationError.cs index 38b729616..db095bad9 100644 --- a/Dalamud/Plugin/Ipc/Exceptions/DataCacheCreationError.cs +++ b/Dalamud/Plugin/Ipc/Exceptions/DataCacheCreationError.cs @@ -1,5 +1,3 @@ -using Dalamud.Plugin.Ipc.Internal; - namespace Dalamud.Plugin.Ipc.Exceptions; /// <summary> @@ -11,11 +9,11 @@ public class DataCacheCreationError : IpcError /// Initializes a new instance of the <see cref="DataCacheCreationError"/> class. /// </summary> /// <param name="tag">Tag of the data cache.</param> - /// <param name="creatorPluginId">The plugin ID of the creating plugin.</param> + /// <param name="creator">The assembly name of the caller.</param> /// <param name="expectedType">The type expected.</param> /// <param name="ex">The thrown exception.</param> - public DataCacheCreationError(string tag, DataCachePluginId creatorPluginId, Type expectedType, Exception ex) - : base($"The creation of the {expectedType} data cache {tag} initialized by {creatorPluginId.InternalName} ({creatorPluginId.EffectiveWorkingId}) was unsuccessful.", ex) + public DataCacheCreationError(string tag, string creator, Type expectedType, Exception ex) + : base($"The creation of the {expectedType} data cache {tag} initialized by {creator} was unsuccessful.", ex) { } } diff --git a/Dalamud/Plugin/Ipc/Exceptions/DataCacheTypeMismatchError.cs b/Dalamud/Plugin/Ipc/Exceptions/DataCacheTypeMismatchError.cs index bfe09b120..e5d9cc4db 100644 --- a/Dalamud/Plugin/Ipc/Exceptions/DataCacheTypeMismatchError.cs +++ b/Dalamud/Plugin/Ipc/Exceptions/DataCacheTypeMismatchError.cs @@ -1,5 +1,3 @@ -using Dalamud.Plugin.Ipc.Internal; - namespace Dalamud.Plugin.Ipc.Exceptions; /// <summary> @@ -11,11 +9,11 @@ public class DataCacheTypeMismatchError : IpcError /// Initializes a new instance of the <see cref="DataCacheTypeMismatchError"/> class. /// </summary> /// <param name="tag">Tag of the data cache.</param> - /// <param name="creatorPluginId">The plugin ID of the creating plugin.</param> + /// <param name="creator">Assembly name of the plugin creating the cache.</param> /// <param name="requestedType">The requested type.</param> /// <param name="actualType">The stored type.</param> - public DataCacheTypeMismatchError(string tag, DataCachePluginId creatorPluginId, Type requestedType, Type actualType) - : base($"Data cache {tag} was requested with type {requestedType}, but {creatorPluginId.InternalName} ({creatorPluginId.EffectiveWorkingId}) created type {actualType}.") + public DataCacheTypeMismatchError(string tag, string creator, Type requestedType, Type actualType) + : base($"Data cache {tag} was requested with type {requestedType}, but {creator} created type {actualType}.") { } } diff --git a/Dalamud/Plugin/Ipc/ICallGateProvider.cs b/Dalamud/Plugin/Ipc/ICallGateProvider.cs index 5fde2ef01..f4e5c76d7 100644 --- a/Dalamud/Plugin/Ipc/ICallGateProvider.cs +++ b/Dalamud/Plugin/Ipc/ICallGateProvider.cs @@ -1,4 +1,5 @@ using Dalamud.Plugin.Ipc.Internal; +using Dalamud.Utility; #pragma warning disable SA1402 // File may only contain a single type @@ -18,9 +19,6 @@ public interface ICallGateProvider /// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/> public void UnregisterFunc(); - - /// <inheritdoc cref="CallGatePubSubBase.GetContext"/> - public IpcContext? GetContext(); } /// <inheritdoc cref="ICallGateProvider"/> diff --git a/Dalamud/Plugin/Ipc/Internal/CallGate.cs b/Dalamud/Plugin/Ipc/Internal/CallGate.cs index 3f299da9d..fef4b97d0 100644 --- a/Dalamud/Plugin/Ipc/Internal/CallGate.cs +++ b/Dalamud/Plugin/Ipc/Internal/CallGate.cs @@ -9,7 +9,7 @@ namespace Dalamud.Plugin.Ipc.Internal; [ServiceManager.EarlyLoadedService] internal class CallGate : IServiceType { - private readonly Dictionary<string, CallGateChannel> gates = []; + private readonly Dictionary<string, CallGateChannel> gates = new(); private ImmutableDictionary<string, CallGateChannel>? gatesCopy; diff --git a/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs b/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs index 78c7f841b..ea94103f7 100644 --- a/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs +++ b/Dalamud/Plugin/Ipc/Internal/CallGateChannel.cs @@ -2,14 +2,11 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Reflection; -using System.Threading; -using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Ipc.Exceptions; using Dalamud.Plugin.Ipc.Internal.Converters; using Newtonsoft.Json; - using Serilog; namespace Dalamud.Plugin.Ipc.Internal; @@ -19,12 +16,10 @@ namespace Dalamud.Plugin.Ipc.Internal; /// </summary> internal class CallGateChannel { - private readonly ThreadLocal<IpcContext> ipcExecutionContext = new(); - /// <summary> /// The actual storage. /// </summary> - private readonly HashSet<Delegate> subscriptions = []; + private readonly HashSet<Delegate> subscriptions = new(); /// <summary> /// A copy of the actual storage, that will be cleared and populated depending on changes made to @@ -150,32 +145,6 @@ internal class CallGateChannel return (TRet)result; } - /// <summary> - /// Set the context for the invocations through this channel. - /// </summary> - /// <param name="ipcContext">The context to set.</param> - internal void SetInvocationContext(IpcContext ipcContext) - { - this.ipcExecutionContext.Value = ipcContext; - } - - /// <summary> - /// Get the context for invocations through this channel. - /// </summary> - /// <returns>The context, if one was set.</returns> - internal IpcContext? GetInvocationContext() - { - return this.ipcExecutionContext.IsValueCreated ? this.ipcExecutionContext.Value : null; - } - - /// <summary> - /// Clear the context for this channel. - /// </summary> - internal void ClearInvocationContext() - { - this.ipcExecutionContext.Value = null; - } - private void CheckAndConvertArgs(object?[]? args, MethodInfo methodInfo) { var paramTypes = methodInfo.GetParameters() diff --git a/Dalamud/Plugin/Ipc/Internal/CallGatePubSub.cs b/Dalamud/Plugin/Ipc/Internal/CallGatePubSub.cs index 8725ef733..cc54a563b 100644 --- a/Dalamud/Plugin/Ipc/Internal/CallGatePubSub.cs +++ b/Dalamud/Plugin/Ipc/Internal/CallGatePubSub.cs @@ -1,5 +1,3 @@ -using Dalamud.Plugin.Internal.Types; - #pragma warning disable SA1402 // File may only contain a single type namespace Dalamud.Plugin.Ipc.Internal; @@ -7,9 +5,9 @@ namespace Dalamud.Plugin.Ipc.Internal; /// <inheritdoc cref="CallGatePubSubBase"/> internal class CallGatePubSub<TRet> : CallGatePubSubBase, ICallGateProvider<TRet>, ICallGateSubscriber<TRet> { - /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/> - public CallGatePubSub(string name, LocalPlugin? owningPlugin = null) - : base(name, owningPlugin) + /// <inheritdoc cref="CallGatePubSubBase(string)"/> + public CallGatePubSub(string name) + : base(name) { } @@ -45,9 +43,9 @@ internal class CallGatePubSub<TRet> : CallGatePubSubBase, ICallGateProvider<TRet /// <inheritdoc cref="CallGatePubSubBase"/> internal class CallGatePubSub<T1, TRet> : CallGatePubSubBase, ICallGateProvider<T1, TRet>, ICallGateSubscriber<T1, TRet> { - /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/> - public CallGatePubSub(string name, LocalPlugin? owningPlugin = null) - : base(name, owningPlugin) + /// <inheritdoc cref="CallGatePubSubBase(string)"/> + public CallGatePubSub(string name) + : base(name) { } @@ -83,9 +81,9 @@ internal class CallGatePubSub<T1, TRet> : CallGatePubSubBase, ICallGateProvider< /// <inheritdoc cref="CallGatePubSubBase"/> internal class CallGatePubSub<T1, T2, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, TRet>, ICallGateSubscriber<T1, T2, TRet> { - /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/> - public CallGatePubSub(string name, LocalPlugin? owningPlugin = null) - : base(name, owningPlugin) + /// <inheritdoc cref="CallGatePubSubBase(string)"/> + public CallGatePubSub(string name) + : base(name) { } @@ -121,9 +119,9 @@ internal class CallGatePubSub<T1, T2, TRet> : CallGatePubSubBase, ICallGateProvi /// <inheritdoc cref="CallGatePubSubBase"/> internal class CallGatePubSub<T1, T2, T3, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, TRet>, ICallGateSubscriber<T1, T2, T3, TRet> { - /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/> - public CallGatePubSub(string name, LocalPlugin? owningPlugin = null) - : base(name, owningPlugin) + /// <inheritdoc cref="CallGatePubSubBase(string)"/> + public CallGatePubSub(string name) + : base(name) { } @@ -159,9 +157,9 @@ internal class CallGatePubSub<T1, T2, T3, TRet> : CallGatePubSubBase, ICallGateP /// <inheritdoc cref="CallGatePubSubBase"/> internal class CallGatePubSub<T1, T2, T3, T4, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, TRet>, ICallGateSubscriber<T1, T2, T3, T4, TRet> { - /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/> - public CallGatePubSub(string name, LocalPlugin? owningPlugin = null) - : base(name, owningPlugin) + /// <inheritdoc cref="CallGatePubSubBase(string)"/> + public CallGatePubSub(string name) + : base(name) { } @@ -197,9 +195,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, TRet> : CallGatePubSubBase, ICallG /// <inheritdoc cref="CallGatePubSubBase"/> internal class CallGatePubSub<T1, T2, T3, T4, T5, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, TRet> { - /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/> - public CallGatePubSub(string name, LocalPlugin? owningPlugin = null) - : base(name, owningPlugin) + /// <inheritdoc cref="CallGatePubSubBase(string)"/> + public CallGatePubSub(string name) + : base(name) { } @@ -235,9 +233,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, T5, TRet> : CallGatePubSubBase, IC /// <inheritdoc cref="CallGatePubSubBase"/> internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet> { - /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/> - public CallGatePubSub(string name, LocalPlugin? owningPlugin = null) - : base(name, owningPlugin) + /// <inheritdoc cref="CallGatePubSubBase(string)"/> + public CallGatePubSub(string name) + : base(name) { } @@ -273,9 +271,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet> : CallGatePubSubBase /// <inheritdoc cref="CallGatePubSubBase"/> internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet> { - /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/> - public CallGatePubSub(string name, LocalPlugin? owningPlugin = null) - : base(name, owningPlugin) + /// <inheritdoc cref="CallGatePubSubBase(string)"/> + public CallGatePubSub(string name) + : base(name) { } @@ -311,9 +309,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet> : CallGatePubSub /// <inheritdoc cref="CallGatePubSubBase"/> internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet> { - /// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/> - public CallGatePubSub(string name, LocalPlugin? owningPlugin = null) - : base(name, owningPlugin) + /// <inheritdoc cref="CallGatePubSubBase(string)"/> + public CallGatePubSub(string name) + : base(name) { } diff --git a/Dalamud/Plugin/Ipc/Internal/CallGatePubSubBase.cs b/Dalamud/Plugin/Ipc/Internal/CallGatePubSubBase.cs index 521824b7b..308457373 100644 --- a/Dalamud/Plugin/Ipc/Internal/CallGatePubSubBase.cs +++ b/Dalamud/Plugin/Ipc/Internal/CallGatePubSubBase.cs @@ -1,11 +1,4 @@ -using System.Reactive.Disposables; -using System.Threading; - -using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Ipc.Exceptions; -using Dalamud.Utility; - -using Serilog; namespace Dalamud.Plugin.Ipc.Internal; @@ -18,11 +11,9 @@ internal abstract class CallGatePubSubBase /// Initializes a new instance of the <see cref="CallGatePubSubBase"/> class. /// </summary> /// <param name="name">The name of the IPC registration.</param> - /// <param name="owningPlugin">The plugin that owns this IPC pubsub.</param> - protected CallGatePubSubBase(string name, LocalPlugin? owningPlugin) + protected CallGatePubSubBase(string name) { this.Channel = Service<CallGate>.Get().GetOrCreateChannel(name); - this.OwningPlugin = owningPlugin; } /// <summary> @@ -30,7 +21,7 @@ internal abstract class CallGatePubSubBase /// <see cref="ICallGateSubscriber"/>s. /// </summary> public bool HasAction => this.Channel.Action != null; - + /// <summary> /// Gets a value indicating whether this IPC call gate has an associated Function. Only exposed to /// <see cref="ICallGateSubscriber"/>s. @@ -42,17 +33,12 @@ internal abstract class CallGatePubSubBase /// <see cref="ICallGateProvider"/>s, and can be used to determine if messages should be sent through the gate. /// </summary> public int SubscriptionCount => this.Channel.Subscriptions.Count; - + /// <summary> /// Gets the underlying channel implementation. /// </summary> protected CallGateChannel Channel { get; init; } - - /// <summary> - /// Gets the plugin that owns this pubsub instance. - /// </summary> - protected LocalPlugin? OwningPlugin { get; init; } - + /// <summary> /// Removes the associated Action from this call gate, effectively disabling RPC calls. /// </summary> @@ -67,16 +53,6 @@ internal abstract class CallGatePubSubBase public void UnregisterFunc() => this.Channel.Func = null; - /// <summary> - /// Gets the current context for this IPC call. This will only be present when called from within an IPC action - /// or function handler, and will be null otherwise. - /// </summary> - /// <returns>Returns a potential IPC context.</returns> - public IpcContext? GetContext() - { - return this.Channel.GetInvocationContext(); - } - /// <summary> /// Registers a <see cref="Delegate"/> for use by other plugins via RPC. This Delegate must satisfy the constraints /// of an <see cref="Action"/> type as defined by the interface, meaning they may not return a value and must have @@ -129,12 +105,7 @@ internal abstract class CallGatePubSubBase /// <seealso cref="RegisterAction"/> /// <seealso cref="UnregisterAction"/> private protected void InvokeAction(params object?[]? args) - { - using (this.BuildContext()) - { - this.Channel.InvokeAction(args); - } - } + => this.Channel.InvokeAction(args); /// <summary> /// Executes the Function registered for this IPC call gate via <see cref="RegisterFunc"/>. This method is intended @@ -149,12 +120,7 @@ internal abstract class CallGatePubSubBase /// <seealso cref="RegisterFunc"/> /// <seealso cref="UnregisterFunc"/> private protected TRet InvokeFunc<TRet>(params object?[]? args) - { - using (this.BuildContext()) - { - return this.Channel.InvokeFunc<TRet>(args); - } - } + => this.Channel.InvokeFunc<TRet>(args); /// <summary> /// Send the given arguments to all subscribers (through <see cref="Subscribe"/>) of this IPC call gate. This method @@ -166,14 +132,4 @@ internal abstract class CallGatePubSubBase /// <param name="args">Delegate arguments.</param> private protected void SendMessage(params object?[]? args) => this.Channel.SendMessage(args); - - private IDisposable BuildContext() - { - this.Channel.SetInvocationContext(new IpcContext - { - SourcePlugin = this.OwningPlugin != null ? new ExposedPlugin(this.OwningPlugin) : null, - }); - - return Disposable.Create(() => { this.Channel.ClearInvocationContext(); }); - } } diff --git a/Dalamud/Plugin/Ipc/Internal/DataCache.cs b/Dalamud/Plugin/Ipc/Internal/DataCache.cs index cbb5bb342..38cea4866 100644 --- a/Dalamud/Plugin/Ipc/Internal/DataCache.cs +++ b/Dalamud/Plugin/Ipc/Internal/DataCache.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Runtime.ExceptionServices; using Dalamud.Plugin.Ipc.Exceptions; @@ -17,12 +16,12 @@ internal readonly struct DataCache /// <summary> Name of the data. </summary> internal readonly string Tag; - /// <summary> The creating plugin ID of this DataCache entry. </summary> - internal readonly DataCachePluginId CreatorPluginId; + /// <summary> The assembly name of the initial creator. </summary> + internal readonly string CreatorAssemblyName; - /// <summary> A distinct list of plugin IDs that are using this data. </summary> + /// <summary> A not-necessarily distinct list of current users. </summary> /// <remarks> Also used as a reference count tracker. </remarks> - internal readonly List<DataCachePluginId> UserPluginIds; + internal readonly List<string> UserAssemblyNames; /// <summary> The type the data was registered as. </summary> internal readonly Type Type; @@ -34,14 +33,14 @@ internal readonly struct DataCache /// Initializes a new instance of the <see cref="DataCache"/> struct. /// </summary> /// <param name="tag">Name of the data.</param> - /// <param name="creatorPluginId">The internal name and effective working ID of the creating plugin.</param> + /// <param name="creatorAssemblyName">The assembly name of the initial creator.</param> /// <param name="data">A reference to data.</param> /// <param name="type">The type of the data.</param> - public DataCache(string tag, DataCachePluginId creatorPluginId, object? data, Type type) + public DataCache(string tag, string creatorAssemblyName, object? data, Type type) { this.Tag = tag; - this.CreatorPluginId = creatorPluginId; - this.UserPluginIds = []; + this.CreatorAssemblyName = creatorAssemblyName; + this.UserAssemblyNames = new(); this.Data = data; this.Type = type; } @@ -50,40 +49,40 @@ internal readonly struct DataCache /// Creates a new instance of the <see cref="DataCache"/> struct, using the given data generator function. /// </summary> /// <param name="tag">The name for the data cache.</param> - /// <param name="creatorPluginId">The internal name and effective working ID of the creating plugin.</param> + /// <param name="creatorAssemblyName">The assembly name of the initial creator.</param> /// <param name="dataGenerator">The function that generates the data if it does not already exist.</param> /// <typeparam name="T">The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.</typeparam> /// <returns>The new instance of <see cref="DataCache"/>.</returns> - public static DataCache From<T>(string tag, DataCachePluginId creatorPluginId, Func<T> dataGenerator) + public static DataCache From<T>(string tag, string creatorAssemblyName, Func<T> dataGenerator) where T : class { try { - var result = new DataCache(tag, creatorPluginId, dataGenerator.Invoke(), typeof(T)); + var result = new DataCache(tag, creatorAssemblyName, dataGenerator.Invoke(), typeof(T)); Log.Verbose( "[{who}] Created new data for [{Tag:l}] for creator {Creator:l}.", nameof(DataShare), tag, - creatorPluginId); + creatorAssemblyName); return result; } catch (Exception e) { throw ExceptionDispatchInfo.SetCurrentStackTrace( - new DataCacheCreationError(tag, creatorPluginId, typeof(T), e)); + new DataCacheCreationError(tag, creatorAssemblyName, typeof(T), e)); } } /// <summary> /// Attempts to fetch the data. /// </summary> - /// <param name="callingPluginId">The calling plugin ID.</param> + /// <param name="callerName">The name of the caller assembly.</param> /// <param name="value">The value, if succeeded.</param> /// <param name="ex">The exception, if failed.</param> /// <typeparam name="T">Desired type of the data.</typeparam> /// <returns><c>true</c> on success.</returns> public bool TryGetData<T>( - DataCachePluginId callingPluginId, + string callerName, [NotNullWhen(true)] out T? value, [NotNullWhen(false)] out Exception? ex) where T : class @@ -99,21 +98,16 @@ internal readonly struct DataCache value = data; ex = null; - // Register the access history. The effective working ID is unique per plugin and persists between reloads, so only add it once. - lock (this.UserPluginIds) - { - if (this.UserPluginIds.All(c => c.EffectiveWorkingId != callingPluginId.EffectiveWorkingId)) - { - this.UserPluginIds.Add(callingPluginId); - } - } + // Register the access history + lock (this.UserAssemblyNames) + this.UserAssemblyNames.Add(callerName); return true; default: value = null; ex = ExceptionDispatchInfo.SetCurrentStackTrace( - new DataCacheTypeMismatchError(this.Tag, this.CreatorPluginId, typeof(T), this.Type)); + new DataCacheTypeMismatchError(this.Tag, this.CreatorAssemblyName, typeof(T), this.Type)); return false; } } diff --git a/Dalamud/Plugin/Ipc/Internal/DataCachePluginId.cs b/Dalamud/Plugin/Ipc/Internal/DataCachePluginId.cs deleted file mode 100644 index c68dc7c06..000000000 --- a/Dalamud/Plugin/Ipc/Internal/DataCachePluginId.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.ExceptionServices; - -using Dalamud.Plugin.Ipc.Exceptions; - -using Serilog; - -namespace Dalamud.Plugin.Ipc.Internal; - -/// <summary> -/// Stores the internal name and effective working ID of a plugin accessing datashare. -/// </summary> -/// <param name="InternalName">The internal name of the plugin.</param> -/// <param name="EffectiveWorkingId">The effective working ID of the plugin.</param> -public record DataCachePluginId(string InternalName, Guid EffectiveWorkingId); diff --git a/Dalamud/Plugin/Ipc/Internal/DataShare.cs b/Dalamud/Plugin/Ipc/Internal/DataShare.cs index ffad4876e..8ce5ddb95 100644 --- a/Dalamud/Plugin/Ipc/Internal/DataShare.cs +++ b/Dalamud/Plugin/Ipc/Internal/DataShare.cs @@ -19,7 +19,7 @@ internal class DataShare : IServiceType /// Dictionary of cached values. Note that <see cref="Lazy{T}"/> is being used, as it does its own locking, /// effectively preventing calling the data generator multiple times concurrently. /// </summary> - private readonly Dictionary<string, Lazy<DataCache>> caches = []; + private readonly Dictionary<string, Lazy<DataCache>> caches = new(); [ServiceManager.ServiceConstructor] private DataShare() @@ -33,23 +33,24 @@ internal class DataShare : IServiceType /// </summary> /// <typeparam name="T">The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.</typeparam> /// <param name="tag">The name for the data cache.</param> - /// <param name="callingPluginId">The ID of the calling plugin.</param> /// <param name="dataGenerator">The function that generates the data if it does not already exist.</param> /// <returns>Either the existing data for <paramref name="tag"/> or the data generated by <paramref name="dataGenerator"/>.</returns> /// <exception cref="DataCacheTypeMismatchError">Thrown if a cache for <paramref name="tag"/> exists, but contains data of a type not assignable to <typeparamref name="T>"/>.</exception> /// <exception cref="DataCacheValueNullError">Thrown if the stored data for a cache is null.</exception> /// <exception cref="DataCacheCreationError">Thrown if <paramref name="dataGenerator"/> throws an exception or returns null.</exception> - public T GetOrCreateData<T>(string tag, DataCachePluginId callingPluginId, Func<T> dataGenerator) + public T GetOrCreateData<T>(string tag, Func<T> dataGenerator) where T : class { + var callerName = GetCallerName(); + Lazy<DataCache> cacheLazy; lock (this.caches) { if (!this.caches.TryGetValue(tag, out cacheLazy)) - this.caches[tag] = cacheLazy = new(() => DataCache.From(tag, callingPluginId, dataGenerator)); + this.caches[tag] = cacheLazy = new(() => DataCache.From(tag, callerName, dataGenerator)); } - return cacheLazy.Value.TryGetData<T>(callingPluginId, out var value, out var ex) ? value : throw ex; + return cacheLazy.Value.TryGetData<T>(callerName, out var value, out var ex) ? value : throw ex; } /// <summary> @@ -57,8 +58,7 @@ internal class DataShare : IServiceType /// If no assembly uses the data anymore, the cache will be removed from the data share and if it is an IDisposable, Dispose will be called on it. /// </summary> /// <param name="tag">The name for the data cache.</param> - /// <param name="callingPluginId">The ID of the calling plugin.</param> - public void RelinquishData(string tag, DataCachePluginId callingPluginId) + public void RelinquishData(string tag) { DataCache cache; lock (this.caches) @@ -66,8 +66,10 @@ internal class DataShare : IServiceType if (!this.caches.TryGetValue(tag, out var cacheLazy)) return; + var callerName = GetCallerName(); + cache = cacheLazy.Value; - if (!cache.UserPluginIds.Remove(callingPluginId) || cache.UserPluginIds.Count > 0) + if (!cache.UserAssemblyNames.Remove(callerName) || cache.UserAssemblyNames.Count > 0) return; if (!this.caches.Remove(tag)) return; @@ -82,7 +84,7 @@ internal class DataShare : IServiceType } catch (Exception e) { - Log.Error(e, "[DataShare] Failed to dispose [{Tag:l}] after it was removed from all shares.", tag); + Log.Error(e, "[DataShare] Failed to dispose [{Tag:l}] after it was removed from all shares.", tag); } } else @@ -97,10 +99,9 @@ internal class DataShare : IServiceType /// </summary> /// <typeparam name="T">The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.</typeparam> /// <param name="tag">The name for the data cache.</param> - /// <param name="callingPluginId">The ID of the calling plugin.</param> /// <param name="data">The requested data on success, null otherwise.</param> /// <returns>True if the requested data exists and is assignable to the requested type.</returns> - public bool TryGetData<T>(string tag, DataCachePluginId callingPluginId, [NotNullWhen(true)] out T? data) + public bool TryGetData<T>(string tag, [NotNullWhen(true)] out T? data) where T : class { data = null; @@ -111,7 +112,7 @@ internal class DataShare : IServiceType return false; } - return cacheLazy.Value.TryGetData(callingPluginId, out data, out _); + return cacheLazy.Value.TryGetData(GetCallerName(), out data, out _); } /// <summary> @@ -120,12 +121,11 @@ internal class DataShare : IServiceType /// </summary> /// <typeparam name="T">The type of the stored data - needs to be a reference type that is shared through Dalamud itself, not loaded by the plugin.</typeparam> /// <param name="tag">The name for the data cache.</param> - /// <param name="callingPluginId">The ID of the calling plugin.</param> /// <returns>The requested data.</returns> /// <exception cref="KeyNotFoundException">Thrown if <paramref name="tag"/> is not registered.</exception> /// <exception cref="DataCacheTypeMismatchError">Thrown if a cache for <paramref name="tag"/> exists, but contains data of a type not assignable to <typeparamref name="T>"/>.</exception> /// <exception cref="DataCacheValueNullError">Thrown if the stored data for a cache is null.</exception> - public T GetData<T>(string tag, DataCachePluginId callingPluginId) + public T GetData<T>(string tag) where T : class { Lazy<DataCache> cacheLazy; @@ -135,19 +135,35 @@ internal class DataShare : IServiceType throw new KeyNotFoundException($"The data cache [{tag}] is not registered."); } - return cacheLazy.Value.TryGetData<T>(callingPluginId, out var value, out var ex) ? value : throw ex; + return cacheLazy.Value.TryGetData<T>(GetCallerName(), out var value, out var ex) ? value : throw ex; } /// <summary> /// Obtain a read-only list of data shares. /// </summary> /// <returns>All currently subscribed tags, their creator names and all their users.</returns> - internal IEnumerable<(string Tag, DataCachePluginId CreatorPluginId, DataCachePluginId[] UserPluginIds)> GetAllShares() + internal IEnumerable<(string Tag, string CreatorAssembly, string[] Users)> GetAllShares() { lock (this.caches) { return this.caches.Select( - kvp => (kvp.Key, kvp.Value.Value.CreatorPluginId, kvp.Value.Value.UserPluginIds.ToArray())); + kvp => (kvp.Key, kvp.Value.Value.CreatorAssemblyName, kvp.Value.Value.UserAssemblyNames.ToArray())); } } + + /// <summary> Obtain the last assembly name in the stack trace that is not a system or dalamud assembly. </summary> + private static string GetCallerName() + { + var frames = new StackTrace().GetFrames(); + foreach (var frame in frames.Reverse()) + { + var name = frame.GetMethod()?.DeclaringType?.Assembly.GetName().Name ?? "Unknown"; + if (!name.StartsWith("System") && !name.StartsWith("Dalamud")) + { + return name; + } + } + + return "Unknown"; + } } diff --git a/Dalamud/Plugin/Ipc/IpcContext.cs b/Dalamud/Plugin/Ipc/IpcContext.cs deleted file mode 100644 index 25fde6a36..000000000 --- a/Dalamud/Plugin/Ipc/IpcContext.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Dalamud.Plugin.Ipc; - -/// <summary> -/// The context associated for an IPC call. Reads from ThreadLocal. -/// </summary> -public class IpcContext -{ - /// <summary> - /// Gets the plugin that initiated this IPC call. - /// </summary> - public IExposedPlugin? SourcePlugin { get; init; } - - /// <inheritdoc/> - public override string ToString() => $"<IpcContext SourcePlugin={this.SourcePlugin?.Name ?? "null"}>"; -} diff --git a/Dalamud/Plugin/Services/ISelfTestRegistry.cs b/Dalamud/Plugin/SelfTest/ISelfTestRegistry.cs similarity index 92% rename from Dalamud/Plugin/Services/ISelfTestRegistry.cs rename to Dalamud/Plugin/SelfTest/ISelfTestRegistry.cs index 50d3d35ce..af3b583c9 100644 --- a/Dalamud/Plugin/Services/ISelfTestRegistry.cs +++ b/Dalamud/Plugin/SelfTest/ISelfTestRegistry.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; -using Dalamud.Plugin.SelfTest; - -namespace Dalamud.Plugin.Services; +namespace Dalamud.Plugin.SelfTest; /// <summary> /// Interface for registering and unregistering self-test steps from plugins. @@ -46,7 +44,7 @@ namespace Dalamud.Plugin.Services; /// } /// </code> /// </example> -public interface ISelfTestRegistry : IDalamudService +public interface ISelfTestRegistry { /// <summary> /// Registers the self-test steps for this plugin. diff --git a/Dalamud/Plugin/Services/IAddonLifecycle.cs b/Dalamud/Plugin/Services/IAddonLifecycle.cs index aeff29811..1269b13dc 100644 --- a/Dalamud/Plugin/Services/IAddonLifecycle.cs +++ b/Dalamud/Plugin/Services/IAddonLifecycle.cs @@ -17,7 +17,7 @@ public interface IAddonLifecycle : IDalamudService /// <param name="type">The event type that triggered the message.</param> /// <param name="args">Information about what addon triggered the message.</param> public delegate void AddonEventDelegate(AddonEvent type, AddonArgs args); - + /// <summary> /// Register a listener that will trigger on the specified event and any of the specified addons. /// </summary> @@ -25,7 +25,7 @@ public interface IAddonLifecycle : IDalamudService /// <param name="addonNames">Addon names that will trigger the handler to be invoked.</param> /// <param name="handler">The handler to invoke.</param> void RegisterListener(AddonEvent eventType, IEnumerable<string> addonNames, AddonEventDelegate handler); - + /// <summary> /// Register a listener that will trigger on the specified event only for the specified addon. /// </summary> @@ -33,14 +33,14 @@ public interface IAddonLifecycle : IDalamudService /// <param name="addonName">The addon name that will trigger the handler to be invoked.</param> /// <param name="handler">The handler to invoke.</param> void RegisterListener(AddonEvent eventType, string addonName, AddonEventDelegate handler); - + /// <summary> /// Register a listener that will trigger on the specified event for any addon. /// </summary> /// <param name="eventType">Event type to trigger on.</param> /// <param name="handler">The handler to invoke.</param> void RegisterListener(AddonEvent eventType, AddonEventDelegate handler); - + /// <summary> /// Unregister listener from specified event type and specified addon names. /// </summary> @@ -51,7 +51,7 @@ public interface IAddonLifecycle : IDalamudService /// <param name="addonNames">Addon names to deregister.</param> /// <param name="handler">Optional specific handler to remove.</param> void UnregisterListener(AddonEvent eventType, IEnumerable<string> addonNames, [Optional] AddonEventDelegate handler); - + /// <summary> /// Unregister all listeners for the specified event type and addon name. /// </summary> @@ -62,7 +62,7 @@ public interface IAddonLifecycle : IDalamudService /// <param name="addonName">Addon name to deregister.</param> /// <param name="handler">Optional specific handler to remove.</param> void UnregisterListener(AddonEvent eventType, string addonName, [Optional] AddonEventDelegate handler); - + /// <summary> /// Unregister an event type handler.<br/>This will only remove a handler that is added via <see cref="RegisterListener(AddonEvent, AddonEventDelegate)"/>. /// </summary> @@ -72,17 +72,10 @@ public interface IAddonLifecycle : IDalamudService /// <param name="eventType">Event type to deregister.</param> /// <param name="handler">Optional specific handler to remove.</param> void UnregisterListener(AddonEvent eventType, [Optional] AddonEventDelegate handler); - + /// <summary> /// Unregister all events that use the specified handlers. /// </summary> /// <param name="handlers">Handlers to remove.</param> void UnregisterListener(params AddonEventDelegate[] handlers); - - /// <summary> - /// Resolves an addons virtual table address back to the original unmodified table address. - /// </summary> - /// <param name="virtualTableAddress">The address of a modified addons virtual table.</param> - /// <returns>The address of the addons original virtual table.</returns> - nint GetOriginalVirtualTable(nint virtualTableAddress); } diff --git a/Dalamud/Plugin/Services/IAgentLifecycle.cs b/Dalamud/Plugin/Services/IAgentLifecycle.cs deleted file mode 100644 index 62178408d..000000000 --- a/Dalamud/Plugin/Services/IAgentLifecycle.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Collections.Generic; -using System.Runtime.InteropServices; - -using Dalamud.Game.Agent; -using Dalamud.Game.Agent.AgentArgTypes; - -namespace Dalamud.Plugin.Services; - -/// <summary> -/// This class provides events for in-game agent lifecycles. -/// </summary> -public interface IAgentLifecycle : IDalamudService -{ - /// <summary> - /// Delegate for receiving agent lifecycle event messages. - /// </summary> - /// <param name="type">The event type that triggered the message.</param> - /// <param name="args">Information about what agent triggered the message.</param> - public delegate void AgentEventDelegate(AgentEvent type, AgentArgs args); - - /// <summary> - /// Register a listener that will trigger on the specified event and any of the specified agent. - /// </summary> - /// <param name="eventType">Event type to trigger on.</param> - /// <param name="agentIds">Agent IDs that will trigger the handler to be invoked.</param> - /// <param name="handler">The handler to invoke.</param> - void RegisterListener(AgentEvent eventType, IEnumerable<AgentId> agentIds, AgentEventDelegate handler); - - /// <summary> - /// Register a listener that will trigger on the specified event only for the specified agent. - /// </summary> - /// <param name="eventType">Event type to trigger on.</param> - /// <param name="agentId">The agent ID that will trigger the handler to be invoked.</param> - /// <param name="handler">The handler to invoke.</param> - void RegisterListener(AgentEvent eventType, AgentId agentId, AgentEventDelegate handler); - - /// <summary> - /// Register a listener that will trigger on the specified event for any agent. - /// </summary> - /// <param name="eventType">Event type to trigger on.</param> - /// <param name="handler">The handler to invoke.</param> - void RegisterListener(AgentEvent eventType, AgentEventDelegate handler); - - /// <summary> - /// Unregister listener from specified event type and specified agent IDs. - /// </summary> - /// <remarks> - /// If a specific handler is not provided, all handlers for the event type and agent IDs will be unregistered. - /// </remarks> - /// <param name="eventType">Event type to deregister.</param> - /// <param name="agentIds">Agent IDs to deregister.</param> - /// <param name="handler">Optional specific handler to remove.</param> - void UnregisterListener(AgentEvent eventType, IEnumerable<AgentId> agentIds, [Optional] AgentEventDelegate handler); - - /// <summary> - /// Unregister all listeners for the specified event type and agent ID. - /// </summary> - /// <remarks> - /// If a specific handler is not provided, all handlers for the event type and agents will be unregistered. - /// </remarks> - /// <param name="eventType">Event type to deregister.</param> - /// <param name="agentId">Agent id to deregister.</param> - /// <param name="handler">Optional specific handler to remove.</param> - void UnregisterListener(AgentEvent eventType, AgentId agentId, [Optional] AgentEventDelegate handler); - - /// <summary> - /// Unregister an event type handler.<br/>This will only remove a handler that is added via <see cref="RegisterListener(AgentEvent, AgentEventDelegate)"/>. - /// </summary> - /// <remarks> - /// If a specific handler is not provided, all handlers for the event type and agents will be unregistered. - /// </remarks> - /// <param name="eventType">Event type to deregister.</param> - /// <param name="handler">Optional specific handler to remove.</param> - void UnregisterListener(AgentEvent eventType, [Optional] AgentEventDelegate handler); - - /// <summary> - /// Unregister all events that use the specified handlers. - /// </summary> - /// <param name="handlers">Handlers to remove.</param> - void UnregisterListener(params AgentEventDelegate[] handlers); - - /// <summary> - /// Resolves an agents virtual table address back to the original unmodified table address. - /// </summary> - /// <param name="virtualTableAddress">The address of a modified agents virtual table.</param> - /// <returns>The address of the agents original virtual table.</returns> - nint GetOriginalVirtualTable(nint virtualTableAddress); -} diff --git a/Dalamud/Plugin/Services/IChatGui.cs b/Dalamud/Plugin/Services/IChatGui.cs index 2bb7b6913..572ac6c95 100644 --- a/Dalamud/Plugin/Services/IChatGui.cs +++ b/Dalamud/Plugin/Services/IChatGui.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; -using Dalamud.Game.Chat; using Dalamud.Game.Gui; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; @@ -51,12 +50,6 @@ public interface IChatGui : IDalamudService /// <param name="message">The message sent.</param> public delegate void OnMessageUnhandledDelegate(XivChatType type, int timestamp, SeString sender, SeString message); - /// <summary> - /// A delegate type used with the <see cref="IChatGui.LogMessage"/> event. - /// </summary> - /// <param name="message">The message sent.</param> - public delegate void OnLogMessageDelegate(ILogMessage message); - /// <summary> /// Event that will be fired when a chat message is sent to chat by the game. /// </summary> @@ -77,11 +70,6 @@ public interface IChatGui : IDalamudService /// </summary> public event OnMessageUnhandledDelegate ChatMessageUnhandled; - /// <summary> - /// Event that will be fired when a log message, that is a chat message based on entries in the LogMessage sheet, is sent. - /// </summary> - public event OnLogMessageDelegate LogMessage; - /// <summary> /// Gets the ID of the last linked item. /// </summary> diff --git a/Dalamud/Plugin/Services/IClientState.cs b/Dalamud/Plugin/Services/IClientState.cs index 9e7453c25..2555b3b30 100644 --- a/Dalamud/Plugin/Services/IClientState.cs +++ b/Dalamud/Plugin/Services/IClientState.cs @@ -2,7 +2,6 @@ using Dalamud.Game; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Objects.SubKinds; -using Dalamud.Utility; namespace Dalamud.Plugin.Services; @@ -110,14 +109,12 @@ public interface IClientState : IDalamudService /// <summary> /// Gets the local player character, if one is present. /// </summary> - [Api15ToDo("Remove")] [Obsolete($"Use {nameof(IPlayerState)} or {nameof(IObjectTable)}.{nameof(IObjectTable.LocalPlayer)} if necessary.")] public IPlayerCharacter? LocalPlayer { get; } /// <summary> /// Gets the content ID of the local character. /// </summary> - [Api15ToDo("Remove")] [Obsolete($"Use {nameof(IPlayerState)}.{nameof(IPlayerState.ContentId)}")] public ulong LocalContentId { get; } diff --git a/Dalamud/Plugin/Services/IGameNetwork.cs b/Dalamud/Plugin/Services/IGameNetwork.cs new file mode 100644 index 000000000..4abf20834 --- /dev/null +++ b/Dalamud/Plugin/Services/IGameNetwork.cs @@ -0,0 +1,27 @@ +using Dalamud.Game.Network; + +namespace Dalamud.Plugin.Services; + +/// <summary> +/// This class handles interacting with game network events. +/// </summary> +[Obsolete("Will be removed in a future release. Use packet handler hooks instead.", true)] +public interface IGameNetwork : IDalamudService +{ + // TODO(v9): we shouldn't be passing pointers to the actual data here + + /// <summary> + /// The delegate type of a network message event. + /// </summary> + /// <param name="dataPtr">The pointer to the raw data.</param> + /// <param name="opCode">The operation ID code.</param> + /// <param name="sourceActorId">The source actor ID.</param> + /// <param name="targetActorId">The taret actor ID.</param> + /// <param name="direction">The direction of the packed.</param> + public delegate void OnNetworkMessageDelegate(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction); + + /// <summary> + /// Event that is called when a network message is sent/received. + /// </summary> + public event OnNetworkMessageDelegate NetworkMessage; +} diff --git a/Dalamud/Plugin/Services/IPlayerState.cs b/Dalamud/Plugin/Services/IPlayerState.cs index 838d5a346..1416dfb77 100644 --- a/Dalamud/Plugin/Services/IPlayerState.cs +++ b/Dalamud/Plugin/Services/IPlayerState.cs @@ -79,7 +79,7 @@ public interface IPlayerState : IDalamudService bool IsLevelSynced { get; } /// <summary> - /// Gets the effective level of the local character, taking level sync into account. + /// Gets the effective level of the local character. /// </summary> short EffectiveLevel { get; } @@ -159,7 +159,7 @@ public interface IPlayerState : IDalamudService RowRef<Aetheryte> FreeAetheryte { get; } /// <summary> - /// Gets the amount of rested experience available to the local player. + /// Gets the amount of received player commendations of the local player. /// </summary> uint BaseRestedExperience { get; } diff --git a/Dalamud/Plugin/Services/ISigScanner.cs b/Dalamud/Plugin/Services/ISigScanner.cs index 017c4fe9d..fbbd8b05a 100644 --- a/Dalamud/Plugin/Services/ISigScanner.cs +++ b/Dalamud/Plugin/Services/ISigScanner.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.Threading; -namespace Dalamud.Plugin.Services; +using Dalamud.Plugin.Services; + +namespace Dalamud.Game; /// <summary> /// A SigScanner facilitates searching for memory signatures in a given ProcessModule. diff --git a/Dalamud/Plugin/Services/ITargetManager.cs b/Dalamud/Plugin/Services/ITargetManager.cs index 0c14571c5..9c9fce550 100644 --- a/Dalamud/Plugin/Services/ITargetManager.cs +++ b/Dalamud/Plugin/Services/ITargetManager.cs @@ -1,7 +1,7 @@ using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin.Services; -namespace Dalamud.Plugin.Services; +namespace Dalamud.Game.ClientState.Objects; /// <summary> /// Get and set various kinds of targets for the player. @@ -37,13 +37,13 @@ public interface ITargetManager : IDalamudService /// Set to null to clear the target. /// </summary> public IGameObject? SoftTarget { get; set; } - + /// <summary> /// Gets or sets the gpose target. /// Set to null to clear the target. /// </summary> public IGameObject? GPoseTarget { get; set; } - + /// <summary> /// Gets or sets the mouseover nameplate target. /// Set to null to clear the target. diff --git a/Dalamud/Plugin/Services/ITextureProvider.cs b/Dalamud/Plugin/Services/ITextureProvider.cs index 63a463613..7cd1b7c86 100644 --- a/Dalamud/Plugin/Services/ITextureProvider.cs +++ b/Dalamud/Plugin/Services/ITextureProvider.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Dalamud.Bindings.ImGui; -using Dalamud.Interface.ImGuiSeStringRenderer; using Dalamud.Interface.Internal.Windows.Data.Widgets; using Dalamud.Interface.Textures; using Dalamud.Interface.Textures.TextureWraps; @@ -187,17 +186,6 @@ public interface ITextureProvider : IDalamudService string? debugName = null, CancellationToken cancellationToken = default); - /// <summary>Creates a texture by drawing a SeString onto it.</summary> - /// <param name="text">SeString to render.</param> - /// <param name="drawParams">Parameters for drawing.</param> - /// <param name="debugName">Name for debug display purposes.</param> - /// <returns>The new texture.</returns> - /// <remarks>Can be only be used from the main thread.</remarks> - public IDalamudTextureWrap CreateTextureFromSeString( - ReadOnlySpan<byte> text, - scoped in SeStringDrawParams drawParams = default, - string? debugName = null); - /// <summary>Gets the supported bitmap decoders.</summary> /// <returns>The supported bitmap decoders.</returns> /// <remarks> @@ -322,7 +310,7 @@ public interface ITextureProvider : IDalamudService /// <param name="leaveWrapOpen">Whether to leave <paramref name="wrap"/> non-disposed when the returned /// <see cref="Task{TResult}"/> completes.</param> /// <returns>Address of the new <see cref="FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Texture"/>.</returns> - /// <example>See <c>PrintTextureInfo</c> in <see cref="Interface.Internal.UiDebug.Browsing.ImageNodeTree"/> for an example + /// <example>See <c>PrintTextureInfo</c> in <see cref="Interface.Internal.UiDebug.PrintSimpleNode"/> for an example /// of replacing the texture of an image node.</example> /// <remarks> /// <para>If the returned kernel texture is to be destroyed, call the fourth function in its vtable, by calling diff --git a/Dalamud/Plugin/Services/IUnlockState.cs b/Dalamud/Plugin/Services/IUnlockState.cs index 4f92b2b3d..0409843c4 100644 --- a/Dalamud/Plugin/Services/IUnlockState.cs +++ b/Dalamud/Plugin/Services/IUnlockState.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + using Lumina.Excel; using Lumina.Excel.Sheets; @@ -8,6 +10,7 @@ namespace Dalamud.Plugin.Services; /// <summary> /// Interface for determining unlock state of various content in the game. /// </summary> +[Experimental("Dalamud001")] public interface IUnlockState : IDalamudService { /// <summary> @@ -21,24 +24,6 @@ public interface IUnlockState : IDalamudService /// </summary> event UnlockDelegate? Unlock; - /// <summary> - /// Gets a value indicating whether the full Achievements list was received. - /// </summary> - bool IsAchievementListLoaded { get; } - - /// <summary> - /// Gets a value indicating whether the full Titles list was received. - /// </summary> - bool IsTitleListLoaded { get; } - - /// <summary> - /// Determines whether the specified Achievement is completed.<br/> - /// Requires that the player requested the Achievements list (can be chcked with <see cref="IsAchievementListLoaded"/>). - /// </summary> - /// <param name="row">The Achievement row to check.</param> - /// <returns><see langword="true"/> if completed; otherwise, <see langword="false"/>.</returns> - bool IsAchievementComplete(Achievement row); - /// <summary> /// Determines whether the specified Action is unlocked. /// </summary> @@ -46,13 +31,6 @@ public interface IUnlockState : IDalamudService /// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns> bool IsActionUnlocked(Lumina.Excel.Sheets.Action row); - /// <summary> - /// Determines whether the specified Adventure is completed. - /// </summary> - /// <param name="row">The Adventure row to check.</param> - /// <returns><see langword="true"/> if completed; otherwise, <see langword="false"/>.</returns> - public bool IsAdventureComplete(Adventure row); - /// <summary> /// Determines whether the specified AetherCurrentCompFlgSet is unlocked. /// </summary> @@ -228,13 +206,6 @@ public interface IUnlockState : IDalamudService /// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns> bool IsItemUnlocked(Item row); - /// <summary> - /// Determines whether the specified Leve is completed. - /// </summary> - /// <param name="row">The Leve row to check.</param> - /// <returns><see langword="true"/> if completed; otherwise, <see langword="false"/>.</returns> - bool IsLeveCompleted(Leve row); - /// <summary> /// Determines whether the specified McGuffin is unlocked. /// </summary> @@ -298,13 +269,6 @@ public interface IUnlockState : IDalamudService /// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns> bool IsPublicContentUnlocked(PublicContent row); - /// <summary> - /// Determines whether the specified Quest is completed. - /// </summary> - /// <param name="row">The Quest row to check.</param> - /// <returns><see langword="true"/> if completed; otherwise, <see langword="false"/>.</returns> - bool IsQuestCompleted(Quest row); - /// <summary> /// Determines whether the specified Recipe is unlocked. /// </summary> @@ -334,14 +298,6 @@ public interface IUnlockState : IDalamudService /// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns> bool IsSecretRecipeBookUnlocked(SecretRecipeBook row); - /// <summary> - /// Determines whether the specified Title is unlocked.<br/> - /// Requires that the player requested the Titles list (can be chcked with <see cref="IsTitleListLoaded"/>). - /// </summary> - /// <param name="row">The Title row to check.</param> - /// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns> - bool IsTitleUnlocked(Title row); - /// <summary> /// Determines whether the specified Trait is unlocked. /// </summary> diff --git a/Dalamud/Plugin/VersionInfo/DalamudVersionInfo.cs b/Dalamud/Plugin/VersionInfo/DalamudVersionInfo.cs deleted file mode 100644 index 0a6fad9c2..000000000 --- a/Dalamud/Plugin/VersionInfo/DalamudVersionInfo.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Dalamud.Plugin.VersionInfo; - -/// <inheritdoc /> -internal class DalamudVersionInfo(Version version, string? track, string? gitHash, string? gitHashClientStructs, string? scmVersion) : IDalamudVersionInfo -{ - /// <inheritdoc/> - public Version Version { get; } = version; - - /// <inheritdoc/> - public string? BetaTrack { get; } = track; - - /// <inheritdoc/> - public string? GitHash { get; } = gitHash; - - /// <inheritdoc/> - public string? GitHashClientStructs { get; } = gitHashClientStructs; - - /// <inheritdoc/> - public string? ScmVersion { get; } = scmVersion; -} diff --git a/Dalamud/Plugin/VersionInfo/IDalamudVersionInfo.cs b/Dalamud/Plugin/VersionInfo/IDalamudVersionInfo.cs deleted file mode 100644 index 6297ce196..000000000 --- a/Dalamud/Plugin/VersionInfo/IDalamudVersionInfo.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Dalamud.Plugin.VersionInfo; - -/// <summary> -/// Interface exposing various information related to Dalamud versioning. -/// </summary> -public interface IDalamudVersionInfo -{ - /// <summary> - /// Gets the Dalamud version. - /// </summary> - Version Version { get; } - - /// <summary> - /// Gets the currently used beta track. - /// Please don't tell users to switch branches. They have it bad enough, fix your things instead. - /// Null if this build wasn't launched from XIVLauncher. - /// </summary> - string? BetaTrack { get; } - - /// <summary> - /// Gets the git commit hash value from the assembly or null if it cannot be found. Will be null for Debug builds, - /// and will be suffixed with `-dirty` if in release with pending changes. - /// </summary> - string? GitHash { get; } - - /// <summary> - /// Gets the git hash value from the assembly or null if it cannot be found. - /// </summary> - string? GitHashClientStructs { get; } - - /// <summary> - /// Gets the SCM Version from the assembly, or null if it cannot be found. The value returned will generally be - /// the <c>git describe</c> output for this build, which will be a raw version if this is a stable build or an - /// appropriately-annotated version if this is *not* stable. Local builds will return a `Local Build` text string. - /// </summary> - string? ScmVersion { get; } -} diff --git a/Dalamud/SafeMemory.cs b/Dalamud/SafeMemory.cs index 16d16056d..a8ac40a5d 100644 --- a/Dalamud/SafeMemory.cs +++ b/Dalamud/SafeMemory.cs @@ -1,8 +1,6 @@ using System.Runtime.InteropServices; using System.Text; -using Windows.Win32.Foundation; - namespace Dalamud; /// <summary> @@ -14,11 +12,11 @@ namespace Dalamud; /// </remarks> public static class SafeMemory { - private static readonly HANDLE Handle; + private static readonly SafeHandle Handle; static SafeMemory() { - Handle = Windows.Win32.PInvoke.GetCurrentProcess(); + Handle = Windows.Win32.PInvoke.GetCurrentProcess_SafeHandle(); } /// <summary> @@ -30,12 +28,6 @@ public static class SafeMemory /// <returns>Whether the read succeeded.</returns> public static unsafe bool ReadBytes(IntPtr address, int count, out byte[] buffer) { - if (Handle.IsNull) - { - buffer = []; - return false; - } - buffer = new byte[count <= 0 ? 0 : count]; fixed (byte* p = buffer) { @@ -62,9 +54,6 @@ public static class SafeMemory /// <returns>Whether the write succeeded.</returns> public static unsafe bool WriteBytes(IntPtr address, byte[] buffer) { - if (Handle.IsNull) - return false; - if (buffer.Length == 0) return true; @@ -193,7 +182,7 @@ public static class SafeMemory return null; var data = encoding.GetString(buffer); var eosPos = data.IndexOf('\0'); - return eosPos == -1 ? data : data[..eosPos]; + return eosPos == -1 ? data : data.Substring(0, eosPos); } /// <summary> @@ -303,7 +292,7 @@ public static class SafeMemory return bytes; } - public T Read<T>(int offset = 0) => Marshal.PtrToStructure<T>(this.hGlobal + offset); + public T Read<T>(int offset = 0) => (T)Marshal.PtrToStructure(this.hGlobal + offset, typeof(T)); public object? Read(Type type, int offset = 0) => Marshal.PtrToStructure(this.hGlobal + offset, type); diff --git a/Dalamud/Service/LoadingDialog.cs b/Dalamud/Service/LoadingDialog.cs index ea45d3bb2..424087743 100644 --- a/Dalamud/Service/LoadingDialog.cs +++ b/Dalamud/Service/LoadingDialog.cs @@ -1,4 +1,4 @@ -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Drawing; @@ -294,18 +294,18 @@ internal sealed class LoadingDialog ? null : Icon.ExtractAssociatedIcon(Path.Combine(workingDirectory, "Dalamud.Injector.exe")); - fixed (char* pszEmpty = "-") - fixed (char* pszWindowTitle = "Dalamud") - fixed (char* pszDalamudBoot = "Dalamud.Boot.dll") - fixed (char* pszThemesManifestResourceName = "RT_MANIFEST_THEMES") - fixed (char* pszHide = Loc.Localize("LoadingDialogHide", "Hide")) - fixed (char* pszShowLatestLogs = Loc.Localize("LoadingDialogShowLatestLogs", "Show Latest Logs")) - fixed (char* pszHideLatestLogs = Loc.Localize("LoadingDialogHideLatestLogs", "Hide Latest Logs")) + fixed (void* pszEmpty = "-") + fixed (void* pszWindowTitle = "Dalamud") + fixed (void* pszDalamudBoot = "Dalamud.Boot.dll") + fixed (void* pszThemesManifestResourceName = "RT_MANIFEST_THEMES") + fixed (void* pszHide = Loc.Localize("LoadingDialogHide", "Hide")) + fixed (void* pszShowLatestLogs = Loc.Localize("LoadingDialogShowLatestLogs", "Show Latest Logs")) + fixed (void* pszHideLatestLogs = Loc.Localize("LoadingDialogHideLatestLogs", "Hide Latest Logs")) { var taskDialogButton = new TASKDIALOG_BUTTON { nButtonID = IDOK, - pszButtonText = pszHide, + pszButtonText = (ushort*)pszHide, }; var taskDialogConfig = new TASKDIALOGCONFIG { @@ -318,8 +318,8 @@ internal sealed class LoadingDialog (int)TDF_CALLBACK_TIMER | (extractedIcon is null ? 0 : (int)TDF_USE_HICON_MAIN), dwCommonButtons = 0, - pszWindowTitle = pszWindowTitle, - pszMainIcon = extractedIcon is null ? TD.TD_INFORMATION_ICON : (char*)extractedIcon.Handle, + pszWindowTitle = (ushort*)pszWindowTitle, + pszMainIcon = extractedIcon is null ? TD.TD_INFORMATION_ICON : (ushort*)extractedIcon.Handle, pszMainInstruction = null, pszContent = null, cButtons = 1, @@ -329,9 +329,9 @@ internal sealed class LoadingDialog pRadioButtons = null, nDefaultRadioButton = 0, pszVerificationText = null, - pszExpandedInformation = pszEmpty, - pszExpandedControlText = pszShowLatestLogs, - pszCollapsedControlText = pszHideLatestLogs, + pszExpandedInformation = (ushort*)pszEmpty, + pszExpandedControlText = (ushort*)pszShowLatestLogs, + pszCollapsedControlText = (ushort*)pszHideLatestLogs, pszFooterIcon = null, pszFooter = null, pfCallback = &HResultFuncBinder, @@ -348,8 +348,8 @@ internal sealed class LoadingDialog { cbSize = (uint)sizeof(ACTCTXW), dwFlags = ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID, - lpResourceName = pszThemesManifestResourceName, - hModule = GetModuleHandleW(pszDalamudBoot), + lpResourceName = (ushort*)pszThemesManifestResourceName, + hModule = GetModuleHandleW((ushort*)pszDalamudBoot), }; hActCtx = CreateActCtxW(&actctx); if (hActCtx == default) diff --git a/Dalamud/Service/ServiceManager.cs b/Dalamud/Service/ServiceManager.cs index 824f816ff..88c6366fd 100644 --- a/Dalamud/Service/ServiceManager.cs +++ b/Dalamud/Service/ServiceManager.cs @@ -35,7 +35,7 @@ internal static class ServiceManager /// <summary> /// Static log facility for Service{T}, to avoid duplicate instances for different types. /// </summary> - public static readonly ModuleLog Log = new(nameof(ServiceManager)); + public static readonly ModuleLog Log = new("SVC"); #if DEBUG /// <summary> @@ -44,7 +44,7 @@ internal static class ServiceManager internal static readonly ThreadLocal<Type?> CurrentConstructorServiceType = new(); [SuppressMessage("ReSharper", "CollectionNeverQueried.Local", Justification = "Debugging purposes")] - private static readonly List<Type> LoadedServices = []; + private static readonly List<Type> LoadedServices = new(); #endif private static readonly TaskCompletionSource BlockingServicesLoadedTaskCompletionSource = @@ -291,7 +291,7 @@ internal static class ServiceManager await loadingDialog.HideAndJoin(); return; - static async Task WaitWithTimeoutConsent(IEnumerable<Task> tasksEnumerable, LoadingDialog.State state) + async Task WaitWithTimeoutConsent(IEnumerable<Task> tasksEnumerable, LoadingDialog.State state) { loadingDialog.CurrentState = state; var tasks = tasksEnumerable.AsReadOnlyCollection(); @@ -317,7 +317,7 @@ internal static class ServiceManager servicesToLoad.UnionWith(earlyLoadingServices); servicesToLoad.UnionWith(blockingEarlyLoadingServices); - while (servicesToLoad.Count != 0) + while (servicesToLoad.Any()) { foreach (var serviceType in servicesToLoad) { @@ -367,7 +367,7 @@ internal static class ServiceManager BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic, null, null, - [startLoaderArgs])); + new object[] { startLoaderArgs })); servicesToLoad.Remove(serviceType); #if DEBUG @@ -383,23 +383,23 @@ internal static class ServiceManager #endif } - if (tasks.Count == 0) + if (!tasks.Any()) { // No more services we can start loading for now. // Either we're waiting for provided services, or there's a dependency cycle. providedServices.RemoveWhere(x => getAsyncTaskMap[x].IsCompleted); - if (providedServices.Count != 0) + if (providedServices.Any()) await Task.WhenAny(providedServices.Select(x => getAsyncTaskMap[x])); else throw new InvalidOperationException("Unresolvable dependency cycle detected"); continue; } - if (servicesToLoad.Count != 0) + if (servicesToLoad.Any()) { await Task.WhenAny(tasks); var faultedTasks = tasks.Where(x => x.IsFaulted).Select(x => (Exception)x.Exception!).ToArray(); - if (faultedTasks.Length != 0) + if (faultedTasks.Any()) throw new AggregateException(faultedTasks); } else @@ -426,7 +426,7 @@ internal static class ServiceManager await loadingDialog.HideAndJoin(); - while (tasks.Count != 0) + while (tasks.Any()) { await Task.WhenAny(tasks); tasks.RemoveAll(x => x.IsCompleted); diff --git a/Dalamud/Service/Service{T}.cs b/Dalamud/Service/Service{T}.cs index c965eeb3d..1f5558893 100644 --- a/Dalamud/Service/Service{T}.cs +++ b/Dalamud/Service/Service{T}.cs @@ -206,7 +206,7 @@ internal static class Service<T> where T : IServiceType is not ServiceManager.ServiceKind.BlockingEarlyLoadedService and not ServiceManager.ServiceKind.ProvidedService) .ToArray(); - if (offenders.Length != 0) + if (offenders.Any()) { const string bels = nameof(ServiceManager.BlockingEarlyLoadedServiceAttribute); const string ps = nameof(ServiceManager.ProvidedServiceAttribute); @@ -286,13 +286,13 @@ internal static class Service<T> where T : IServiceType { if (method.Invoke(instance, args) is Task task) { - tasks ??= []; + tasks ??= new(); tasks.Add(task); } } catch (Exception e) { - tasks ??= []; + tasks ??= new(); tasks.Add(Task.FromException(e)); } } @@ -351,12 +351,15 @@ internal static class Service<T> where T : IServiceType BindingFlags.CreateInstance | BindingFlags.OptionalParamBinding; return typeof(T) .GetConstructors(ctorBindingFlags) - .SingleOrDefault(x => x.GetCustomAttributes(typeof(ServiceManager.ServiceConstructor), true).Length != 0); + .SingleOrDefault(x => x.GetCustomAttributes(typeof(ServiceManager.ServiceConstructor), true).Any()); } private static async Task<T> ConstructObject(IReadOnlyCollection<object> additionalProvidedTypedObjects) { - var ctor = GetServiceConstructor() ?? throw new Exception($"Service \"{typeof(T).FullName}\" had no applicable constructor"); + var ctor = GetServiceConstructor(); + if (ctor == null) + throw new Exception($"Service \"{typeof(T).FullName}\" had no applicable constructor"); + var args = await ResolveInjectedParameters(ctor.GetParameters(), additionalProvidedTypedObjects) .ConfigureAwait(false); using (Timings.Start($"{typeof(T).Name} Construct")) @@ -457,7 +460,7 @@ internal static class ServiceHelpers BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, null, null, - [includeUnloadDependencies]) ?? new List<Type>(); + new object?[] { includeUnloadDependencies }) ?? new List<Type>(); } /// <summary> diff --git a/Dalamud/Storage/Assets/DalamudAssetPurpose.cs b/Dalamud/Storage/Assets/DalamudAssetPurpose.cs index 69de1f871..e6c7bd920 100644 --- a/Dalamud/Storage/Assets/DalamudAssetPurpose.cs +++ b/Dalamud/Storage/Assets/DalamudAssetPurpose.cs @@ -11,12 +11,12 @@ public enum DalamudAssetPurpose Empty = 0, /// <summary> - /// The asset is a .png file, and can be purposed as a <see cref="TerraFX.Interop.DirectX.ID3D11Texture2D"/>. + /// The asset is a .png file, and can be purposed as a <see cref="SharpDX.Direct3D11.Texture2D"/>. /// </summary> TextureFromPng = 10, - + /// <summary> - /// The asset is a raw texture, and can be purposed as a <see cref="TerraFX.Interop.DirectX.ID3D11Texture2D"/>. + /// The asset is a raw texture, and can be purposed as a <see cref="SharpDX.Direct3D11.Texture2D"/>. /// </summary> TextureFromRaw = 1001, diff --git a/Dalamud/Storage/Assets/IDalamudAssetManager.cs b/Dalamud/Storage/Assets/IDalamudAssetManager.cs index 8b74e4347..b4dc41bfd 100644 --- a/Dalamud/Storage/Assets/IDalamudAssetManager.cs +++ b/Dalamud/Storage/Assets/IDalamudAssetManager.cs @@ -1,8 +1,9 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.IO; using System.Threading.Tasks; +using Dalamud.Interface.Internal; using Dalamud.Interface.Textures.TextureWraps; namespace Dalamud.Storage.Assets; diff --git a/Dalamud/Storage/ReliableFileStorage.cs b/Dalamud/Storage/ReliableFileStorage.cs index 72cc598ae..d9f8526c3 100644 --- a/Dalamud/Storage/ReliableFileStorage.cs +++ b/Dalamud/Storage/ReliableFileStorage.cs @@ -1,11 +1,9 @@ -using System.IO; +using System.IO; using System.Text; -using System.Threading; using System.Threading.Tasks; using Dalamud.Logging.Internal; using Dalamud.Utility; - using SQLite; namespace Dalamud.Storage; @@ -27,9 +25,9 @@ namespace Dalamud.Storage; [ServiceManager.ProvidedService] internal class ReliableFileStorage : IInternalDisposableService { - private static readonly ModuleLog Log = ModuleLog.Create<ReliableFileStorage>(); + private static readonly ModuleLog Log = new("VFS"); - private readonly Lock syncRoot = new(); + private readonly object syncRoot = new(); private SQLiteConnection? db; @@ -274,9 +272,8 @@ internal class ReliableFileStorage : IInternalDisposableService throw new FileNotFoundException("Backup database was not available"); var normalizedPath = NormalizePath(path); - var file = this.db.Table<DbFile>().FirstOrDefault(f => f.Path == normalizedPath && f.ContainerId == containerId) - ?? throw new FileNotFoundException(); - return file.Data; + var file = this.db.Table<DbFile>().FirstOrDefault(f => f.Path == normalizedPath && f.ContainerId == containerId); + return file == null ? throw new FileNotFoundException() : file.Data; } // If the file doesn't exist, immediately check the backup db diff --git a/Dalamud/Support/BugBait.cs b/Dalamud/Support/BugBait.cs index c37c4d2f8..7ce96208c 100644 --- a/Dalamud/Support/BugBait.cs +++ b/Dalamud/Support/BugBait.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using Dalamud.Networking.Http; using Dalamud.Plugin.Internal.Types.Manifest; using Dalamud.Utility; - using Newtonsoft.Json; namespace Dalamud.Support; @@ -38,7 +37,7 @@ internal static class BugBait Name = plugin.InternalName, Version = isTesting ? plugin.TestingAssemblyVersion?.ToString() : plugin.AssemblyVersion.ToString(), Platform = Util.GetHostPlatform().ToString(), - DalamudHash = Versioning.GetScmVersion(), + DalamudHash = Util.GetScmVersion(), }; if (includeException) diff --git a/Dalamud/Support/DalamudReleases.cs b/Dalamud/Support/DalamudReleases.cs index 949ebf94a..15e851da2 100644 --- a/Dalamud/Support/DalamudReleases.cs +++ b/Dalamud/Support/DalamudReleases.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Dalamud.Configuration.Internal; using Dalamud.Networking.Http; -using Dalamud.Utility; using Newtonsoft.Json; @@ -16,7 +15,7 @@ namespace Dalamud.Support; internal class DalamudReleases : IServiceType { private const string VersionInfoUrl = "https://kamori.goats.dev/Dalamud/Release/VersionInfo?track={0}"; - + private readonly HappyHttpClient httpClient; private readonly DalamudConfiguration config; @@ -31,24 +30,20 @@ internal class DalamudReleases : IServiceType this.httpClient = httpClient; this.config = config; } - + /// <summary> /// Get the latest version info for the current track. /// </summary> /// <returns>The version info for the current track.</returns> - public async Task<DalamudVersionInfo?> GetVersionForCurrentTrack() + public async Task<DalamudVersionInfo> GetVersionForCurrentTrack() { - var currentTrack = Versioning.GetActiveTrack(); - if (currentTrack.IsNullOrEmpty()) - return null; - - var url = string.Format(VersionInfoUrl, [currentTrack]); + var url = string.Format(VersionInfoUrl, [this.config.DalamudBetaKind]); var response = await this.httpClient.SharedHttpClient.GetAsync(url); response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject<DalamudVersionInfo>(content); } - + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "laziness")] public class DalamudVersionInfo { diff --git a/Dalamud/Support/Troubleshooting.cs b/Dalamud/Support/Troubleshooting.cs index 779754ee8..4af8d5ffc 100644 --- a/Dalamud/Support/Troubleshooting.cs +++ b/Dalamud/Support/Troubleshooting.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text; @@ -9,9 +8,7 @@ using Dalamud.Interface.Internal; using Dalamud.Plugin.Internal; using Dalamud.Plugin.Internal.Types.Manifest; using Dalamud.Utility; - using Newtonsoft.Json; - using Serilog; namespace Dalamud.Support; @@ -35,7 +32,7 @@ public static class Troubleshooting { LastException = exception; - var fixedContext = context?.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); + var fixedContext = context?.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); try { @@ -69,15 +66,14 @@ public static class Troubleshooting { var payload = new TroubleshootingPayload { - Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(), LoadedPlugins = pluginManager?.InstalledPlugins?.Select(x => x.Manifest as LocalPluginManifest)?.OrderByDescending(x => x.InternalName).ToArray(), PluginStates = pluginManager?.InstalledPlugins?.Where(x => !x.IsDev).ToDictionary(x => x.Manifest.InternalName, x => x.IsBanned ? "Banned" : x.State.ToString()), EverStartedLoadingPlugins = pluginManager?.InstalledPlugins.Where(x => x.HasEverStartedLoad).Select(x => x.InternalName).ToList(), - DalamudVersion = Versioning.GetScmVersion(), - DalamudGitHash = Versioning.GetGitHash() ?? "Unknown", + DalamudVersion = Util.GetScmVersion(), + DalamudGitHash = Util.GetGitHash() ?? "Unknown", GameVersion = startInfo.GameVersion?.ToString() ?? "Unknown", Language = startInfo.Language.ToString(), - BetaKey = Versioning.GetActiveTrack(), + BetaKey = configuration.DalamudBetaKey, DoPluginTest = configuration.DoPluginTest, LoadAllApiLevels = pluginManager?.LoadAllApiLevels == true, InterfaceLoaded = interfaceManager?.IsReady ?? false, @@ -87,12 +83,6 @@ public static class Troubleshooting var encodedPayload = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload))); Log.Information($"TROUBLESHOOTING:{encodedPayload}"); - - File.WriteAllText( - Path.Join( - startInfo.LogPath, - "dalamud.troubleshooting.json"), - JsonConvert.SerializeObject(payload, Formatting.Indented)); } catch (Exception ex) { @@ -111,8 +101,6 @@ public static class Troubleshooting private class TroubleshootingPayload { - public long Timestamp { get; set; } - public LocalPluginManifest[]? LoadedPlugins { get; set; } public Dictionary<string, string>? PluginStates { get; set; } @@ -139,7 +127,7 @@ public static class Troubleshooting public bool ForcedMinHook { get; set; } - public List<ThirdPartyRepoSettings> ThirdRepo => []; + public List<ThirdPartyRepoSettings> ThirdRepo => new(); public bool HasThirdRepo { get; set; } } diff --git a/Dalamud/Utility/Api15ToDoAttribute.cs b/Dalamud/Utility/Api13ToDoAttribute.cs similarity index 67% rename from Dalamud/Utility/Api15ToDoAttribute.cs rename to Dalamud/Utility/Api13ToDoAttribute.cs index 646c260e8..576401cda 100644 --- a/Dalamud/Utility/Api15ToDoAttribute.cs +++ b/Dalamud/Utility/Api13ToDoAttribute.cs @@ -2,10 +2,9 @@ namespace Dalamud.Utility; /// <summary> /// Utility class for marking something to be changed for API 13, for ease of lookup. -/// Intended to represent not the upcoming API, but the one after it for more major changes. /// </summary> [AttributeUsage(AttributeTargets.All, Inherited = false)] -internal sealed class Api15ToDoAttribute : Attribute +internal sealed class Api13ToDoAttribute : Attribute { /// <summary> /// Marks that this should be made internal. @@ -13,11 +12,11 @@ internal sealed class Api15ToDoAttribute : Attribute public const string MakeInternal = "Make internal."; /// <summary> - /// Initializes a new instance of the <see cref="Api15ToDoAttribute"/> class. + /// Initializes a new instance of the <see cref="Api13ToDoAttribute"/> class. /// </summary> /// <param name="what">The explanation.</param> /// <param name="what2">The explanation 2.</param> - public Api15ToDoAttribute(string what, string what2 = "") + public Api13ToDoAttribute(string what, string what2 = "") { _ = what; _ = what2; diff --git a/Dalamud/Utility/ArrayExtensions.cs b/Dalamud/Utility/ArrayExtensions.cs index 5444468c6..5b6ce2332 100644 --- a/Dalamud/Utility/ArrayExtensions.cs +++ b/Dalamud/Utility/ArrayExtensions.cs @@ -115,7 +115,8 @@ internal static class ArrayExtensions if (count < 0 || startIndex > list.Count - count) throw new ArgumentOutOfRangeException(nameof(count), count, null); - ArgumentNullException.ThrowIfNull(match); + if (match == null) + throw new ArgumentNullException(nameof(match)); var endIndex = startIndex + count; for (var i = startIndex; i < endIndex; i++) @@ -137,7 +138,8 @@ internal static class ArrayExtensions /// <inheritdoc cref="List{T}.FindLastIndex(int,int,System.Predicate{T})"/> public static int FindLastIndex<T>(this IReadOnlyList<T> list, int startIndex, int count, Predicate<T> match) { - ArgumentNullException.ThrowIfNull(match); + if (match == null) + throw new ArgumentNullException(nameof(match)); if (list.Count == 0) { diff --git a/Dalamud/Utility/ClipboardFormats.cs b/Dalamud/Utility/ClipboardFormats.cs index b80e05dd3..07b6c00d6 100644 --- a/Dalamud/Utility/ClipboardFormats.cs +++ b/Dalamud/Utility/ClipboardFormats.cs @@ -30,8 +30,8 @@ internal static class ClipboardFormats private static unsafe uint ClipboardFormatFromName(ReadOnlySpan<char> name) { uint cf; - fixed (char* p = name) - cf = RegisterClipboardFormatW(p); + fixed (void* p = name) + cf = RegisterClipboardFormatW((ushort*)p); if (cf != 0) return cf; throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()) ?? diff --git a/Dalamud/Utility/CultureFixes.cs b/Dalamud/Utility/CultureFixes.cs index f37844206..133e79c71 100644 --- a/Dalamud/Utility/CultureFixes.cs +++ b/Dalamud/Utility/CultureFixes.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; namespace Dalamud.Utility; @@ -22,7 +22,7 @@ internal static class CultureFixes // This glyph is not present in any game fonts and not in the range for our Noto // so it will be rendered as a geta (=) instead. That's a hack, but it works and // doesn't look as weird. - static CultureInfo PatchCulture(CultureInfo info) + CultureInfo PatchCulture(CultureInfo info) { var newCulture = (CultureInfo)info.Clone(); diff --git a/Dalamud/Utility/DateTimeSpanExtensions.cs b/Dalamud/Utility/DateTimeSpanExtensions.cs index d60b17924..3cf8975af 100644 --- a/Dalamud/Utility/DateTimeSpanExtensions.cs +++ b/Dalamud/Utility/DateTimeSpanExtensions.cs @@ -100,7 +100,7 @@ public static class DateTimeSpanExtensions private sealed class ParsedRelativeFormatStrings { - private readonly List<(float MinSeconds, string FormatString)> formatStrings = []; + private readonly List<(float MinSeconds, string FormatString)> formatStrings = new(); public ParsedRelativeFormatStrings(string value) { diff --git a/Dalamud/Utility/DiagnosticUtil.cs b/Dalamud/Utility/DiagnosticUtil.cs index d853a2668..155d5cda7 100644 --- a/Dalamud/Utility/DiagnosticUtil.cs +++ b/Dalamud/Utility/DiagnosticUtil.cs @@ -1,6 +1,8 @@ -using System.Diagnostics; +using System.Diagnostics; using System.Linq; +using Dalamud.Bindings.ImGui; + namespace Dalamud.Utility; /// <summary> diff --git a/Dalamud/Utility/DisposeSafety.cs b/Dalamud/Utility/DisposeSafety.cs index ac7ed07d7..64d31048f 100644 --- a/Dalamud/Utility/DisposeSafety.cs +++ b/Dalamud/Utility/DisposeSafety.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reactive.Disposables; @@ -92,7 +92,7 @@ public static class DisposeSafety new AggregateException( new[] { e }.Concat( (IEnumerable<Exception>)r.Exception?.InnerExceptions - ?? [new OperationCanceledException()]))); + ?? new[] { new OperationCanceledException() }))); } } @@ -125,7 +125,7 @@ public static class DisposeSafety } catch (Exception de) { - exceptions ??= []; + exceptions ??= new(); exceptions.Add(de); } } @@ -140,7 +140,7 @@ public static class DisposeSafety /// </summary> public class ScopedFinalizer : IDisposeCallback, IAsyncDisposable { - private readonly List<object> objects = []; + private readonly List<object> objects = new(); /// <inheritdoc/> public event Action<IDisposeCallback>? BeforeDispose; @@ -338,7 +338,7 @@ public static class DisposeSafety } catch (Exception ex) { - exceptions ??= []; + exceptions ??= new(); exceptions.Add(ex); } } @@ -402,7 +402,7 @@ public static class DisposeSafety } catch (Exception ex) { - exceptions ??= []; + exceptions ??= new(); exceptions.Add(ex); } } diff --git a/Dalamud/Utility/DynamicPriorityQueueLoader.cs b/Dalamud/Utility/DynamicPriorityQueueLoader.cs index c32858bb4..83fd366bb 100644 --- a/Dalamud/Utility/DynamicPriorityQueueLoader.cs +++ b/Dalamud/Utility/DynamicPriorityQueueLoader.cs @@ -15,7 +15,7 @@ internal class DynamicPriorityQueueLoader : IDisposable private readonly Channel<WorkItem> newItemChannel; private readonly Channel<object?> workTokenChannel; - private readonly List<WorkItem> workItemPending = []; + private readonly List<WorkItem> workItemPending = new(); private bool disposing; diff --git a/Dalamud/Utility/ErrorHandling.cs b/Dalamud/Utility/ErrorHandling.cs deleted file mode 100644 index 3c025a12e..000000000 --- a/Dalamud/Utility/ErrorHandling.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Dalamud.Utility; - -/// <summary> -/// Utilities for handling errors inside Dalamud. -/// </summary> -internal static partial class ErrorHandling -{ - /// <summary> - /// Crash the game at this point, and show the crash handler with the supplied context. - /// </summary> - /// <param name="context">The context to show in the crash handler.</param> - public static void CrashWithContext(string context) - { - BootVehRaiseExternalEvent(context); - } - - [LibraryImport("Dalamud.Boot.dll", EntryPoint = "BootVehRaiseExternalEventW", StringMarshalling = StringMarshalling.Utf16)] - private static partial void BootVehRaiseExternalEvent(string info); -} diff --git a/Dalamud/Utility/EventHandlerExtensions.cs b/Dalamud/Utility/EventHandlerExtensions.cs index 415937dbf..285e18fa2 100644 --- a/Dalamud/Utility/EventHandlerExtensions.cs +++ b/Dalamud/Utility/EventHandlerExtensions.cs @@ -4,7 +4,6 @@ using Dalamud.Game; using Dalamud.Game.Gui.ContextMenu; using Dalamud.Game.Gui.NamePlate; using Dalamud.Plugin.Services; - using Serilog; namespace Dalamud.Utility; diff --git a/Dalamud/Utility/FilesystemUtil.cs b/Dalamud/Utility/FilesystemUtil.cs index f1b62ee21..3b4298b37 100644 --- a/Dalamud/Utility/FilesystemUtil.cs +++ b/Dalamud/Utility/FilesystemUtil.cs @@ -1,8 +1,7 @@ -using System.ComponentModel; +using System.ComponentModel; using System.IO; using System.Text; -using Windows.Win32.Foundation; using Windows.Win32.Storage.FileSystem; namespace Dalamud.Utility; @@ -48,39 +47,30 @@ public static class FilesystemUtil // Open the temp file var tempPath = path + ".tmp"; - var tempFile = Windows.Win32.PInvoke.CreateFile( + using var tempFile = Windows.Win32.PInvoke.CreateFile( tempPath, (uint)(FILE_ACCESS_RIGHTS.FILE_GENERIC_READ | FILE_ACCESS_RIGHTS.FILE_GENERIC_WRITE), FILE_SHARE_MODE.FILE_SHARE_NONE, null, FILE_CREATION_DISPOSITION.CREATE_ALWAYS, FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL, - HANDLE.Null); + null); - if (tempFile.IsNull) + if (tempFile.IsInvalid) throw new Win32Exception(); // Write the data uint bytesWritten = 0; - fixed (byte* ptr = bytes) - { - if (!Windows.Win32.PInvoke.WriteFile(tempFile, ptr, (uint)bytes.Length, &bytesWritten, null)) - throw new Win32Exception(); - } + if (!Windows.Win32.PInvoke.WriteFile(tempFile, new ReadOnlySpan<byte>(bytes), &bytesWritten, null)) + throw new Win32Exception(); if (bytesWritten != bytes.Length) - { - Windows.Win32.PInvoke.CloseHandle(tempFile); throw new Exception($"Could not write all bytes to temp file ({bytesWritten} of {bytes.Length})"); - } if (!Windows.Win32.PInvoke.FlushFileBuffers(tempFile)) - { - Windows.Win32.PInvoke.CloseHandle(tempFile); throw new Win32Exception(); - } - Windows.Win32.PInvoke.CloseHandle(tempFile); + tempFile.Close(); if (!Windows.Win32.PInvoke.MoveFileEx(tempPath, path, MOVE_FILE_FLAGS.MOVEFILE_REPLACE_EXISTING | MOVE_FILE_FLAGS.MOVEFILE_WRITE_THROUGH)) throw new Win32Exception(); diff --git a/Dalamud/Utility/FuzzyMatcher.cs b/Dalamud/Utility/FuzzyMatcher.cs index d8b881300..03723da89 100644 --- a/Dalamud/Utility/FuzzyMatcher.cs +++ b/Dalamud/Utility/FuzzyMatcher.cs @@ -1,4 +1,4 @@ -#define BORDER_MATCHING +#define BORDER_MATCHING using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -32,12 +32,18 @@ internal readonly ref struct FuzzyMatcher this.needleFinalPosition = this.needleSpan.Length - 1; this.mode = matchMode; - this.needleSegments = matchMode switch + switch (matchMode) { - MatchMode.FuzzyParts => FindNeedleSegments(this.needleSpan), - MatchMode.Fuzzy or MatchMode.Simple => EmptySegArray, - _ => throw new ArgumentOutOfRangeException(nameof(matchMode), matchMode, null), - }; + case MatchMode.FuzzyParts: + this.needleSegments = FindNeedleSegments(this.needleSpan); + break; + case MatchMode.Fuzzy: + case MatchMode.Simple: + this.needleSegments = EmptySegArray; + break; + default: + throw new ArgumentOutOfRangeException(nameof(matchMode), matchMode, null); + } } private static (int Start, int End)[] FindNeedleSegments(ReadOnlySpan<char> span) diff --git a/Dalamud/Utility/Hash.cs b/Dalamud/Utility/Hash.cs index 952c2305f..08fc78901 100644 --- a/Dalamud/Utility/Hash.cs +++ b/Dalamud/Utility/Hash.cs @@ -1,4 +1,4 @@ -using System.Security.Cryptography; +using System.Security.Cryptography; namespace Dalamud.Utility; @@ -24,9 +24,10 @@ public static class Hash /// <returns>The computed hash.</returns> internal static string GetSha256Hash(byte[] buffer) { - var hash = SHA256.HashData(buffer); + using var sha = SHA256.Create(); + var hash = sha.ComputeHash(buffer); return ByteArrayToString(hash); } - private static string ByteArrayToString(byte[] ba) => Convert.ToHexString(ba); + private static string ByteArrayToString(byte[] ba) => BitConverter.ToString(ba).Replace("-", string.Empty); } diff --git a/Dalamud/Utility/ItemUtil.cs b/Dalamud/Utility/ItemUtil.cs index 8ee465486..5f718bcee 100644 --- a/Dalamud/Utility/ItemUtil.cs +++ b/Dalamud/Utility/ItemUtil.cs @@ -3,8 +3,8 @@ using System.Runtime.CompilerServices; using Dalamud.Data; using Dalamud.Game; using Dalamud.Game.Text; - using Lumina.Excel.Sheets; +using Lumina.Text; using Lumina.Text.ReadOnly; namespace Dalamud.Utility; @@ -125,15 +125,10 @@ public static class ItemUtil if (IsEventItem(itemId)) { - // Only English, German, and French have a Name field. - // For other languages, the Name is an empty string, and the Singular field should be used instead. - language ??= dataManager.Language; - var useSingular = language is not (ClientLanguage.English or ClientLanguage.German or ClientLanguage.French); - return dataManager .GetExcelSheet<EventItem>(language) .TryGetRow(itemId, out var eventItem) - ? (useSingular ? eventItem.Singular : eventItem.Name) + ? eventItem.Name : default; } @@ -149,21 +144,23 @@ public static class ItemUtil if (!includeIcon || kind is not (ItemKind.Hq or ItemKind.Collectible)) return item.Name; - using var rssb = new RentedSeStringBuilder(); + var builder = SeStringBuilder.SharedPool.Get(); - rssb.Builder.Append(item.Name); + builder.Append(item.Name); switch (kind) { case ItemKind.Hq: - rssb.Builder.Append($" {(char)SeIconChar.HighQuality}"); + builder.Append($" {(char)SeIconChar.HighQuality}"); break; case ItemKind.Collectible: - rssb.Builder.Append($" {(char)SeIconChar.Collectible}"); + builder.Append($" {(char)SeIconChar.Collectible}"); break; } - return rssb.Builder.ToReadOnlySeString(); + var itemName = builder.ToReadOnlySeString(); + SeStringBuilder.SharedPool.Return(builder); + return itemName; } /// <summary> diff --git a/Dalamud/Utility/RollingList.cs b/Dalamud/Utility/RollingList.cs index 896b74fbf..0f1553bf9 100644 --- a/Dalamud/Utility/RollingList.cs +++ b/Dalamud/Utility/RollingList.cs @@ -40,7 +40,7 @@ namespace Dalamud.Utility { ThrowHelper.ThrowArgumentOutOfRangeExceptionIfLessThan(nameof(size), size, 0); this.size = size; - this.items = []; + this.items = new(); } /// <summary>Initializes a new instance of the <see cref="RollingList{T}"/> class.</summary> diff --git a/Dalamud/Utility/SeStringExtensions.cs b/Dalamud/Utility/SeStringExtensions.cs index 9de116f26..cd095c467 100644 --- a/Dalamud/Utility/SeStringExtensions.cs +++ b/Dalamud/Utility/SeStringExtensions.cs @@ -1,3 +1,7 @@ +using System.Linq; + +using InteropGenerator.Runtime; + using Lumina.Text.Parse; using Lumina.Text.ReadOnly; @@ -45,9 +49,11 @@ public static class SeStringExtensions /// <returns><c>this</c> for method chaining.</returns> public static DSeStringBuilder AppendMacroString(this DSeStringBuilder ssb, ReadOnlySpan<byte> macroString) { - using var rssb = new RentedSeStringBuilder(); - rssb.Builder.AppendMacroString(macroString, new() { ExceptionMode = MacroStringParseExceptionMode.EmbedError }); - return ssb.Append(DSeString.Parse(rssb.Builder.ToReadOnlySeString().Data.Span)); + var lssb = LSeStringBuilder.SharedPool.Get(); + lssb.AppendMacroString(macroString, new() { ExceptionMode = MacroStringParseExceptionMode.EmbedError }); + ssb.Append(DSeString.Parse(lssb.ToReadOnlySeString().Data.Span)); + LSeStringBuilder.SharedPool.Return(lssb); + return ssb; } /// <summary>Compiles and appends a macro string.</summary> @@ -56,9 +62,11 @@ public static class SeStringExtensions /// <returns><c>this</c> for method chaining.</returns> public static DSeStringBuilder AppendMacroString(this DSeStringBuilder ssb, ReadOnlySpan<char> macroString) { - using var rssb = new RentedSeStringBuilder(); - rssb.Builder.AppendMacroString(macroString, new() { ExceptionMode = MacroStringParseExceptionMode.EmbedError }); - return ssb.Append(DSeString.Parse(rssb.Builder.ToReadOnlySeString().Data.Span)); + var lssb = LSeStringBuilder.SharedPool.Get(); + lssb.AppendMacroString(macroString, new() { ExceptionMode = MacroStringParseExceptionMode.EmbedError }); + ssb.Append(DSeString.Parse(lssb.ToReadOnlySeString().Data.Span)); + LSeStringBuilder.SharedPool.Return(lssb); + return ssb; } /// <summary> @@ -155,7 +163,7 @@ public static class SeStringExtensions if (ross.IsEmpty) return ross; - using var rssb = new RentedSeStringBuilder(); + var sb = LSeStringBuilder.SharedPool.Get(); foreach (var payload in ross) { @@ -164,25 +172,25 @@ public static class SeStringExtensions if (payload.Type != ReadOnlySePayloadType.Text) { - rssb.Builder.Append(payload); + sb.Append(payload); continue; } var index = payload.Body.Span.IndexOf(toFind); if (index == -1) { - rssb.Builder.Append(payload); + sb.Append(payload); continue; } var lastIndex = 0; while (index != -1) { - rssb.Builder.Append(payload.Body.Span[lastIndex..index]); + sb.Append(payload.Body.Span[lastIndex..index]); if (!replacement.IsEmpty) { - rssb.Builder.Append(replacement); + sb.Append(replacement); } lastIndex = index + toFind.Length; @@ -192,10 +200,12 @@ public static class SeStringExtensions index += lastIndex; } - rssb.Builder.Append(payload.Body.Span[lastIndex..]); + sb.Append(payload.Body.Span[lastIndex..]); } - return rssb.Builder.ToReadOnlySeString(); + var output = sb.ToReadOnlySeString(); + LSeStringBuilder.SharedPool.Return(sb); + return output; } /// <summary> diff --git a/Dalamud/Utility/Signatures/SignatureHelper.cs b/Dalamud/Utility/Signatures/SignatureHelper.cs index e1a4dafc2..cdf601852 100755 --- a/Dalamud/Utility/Signatures/SignatureHelper.cs +++ b/Dalamud/Utility/Signatures/SignatureHelper.cs @@ -5,8 +5,8 @@ using System.Runtime.InteropServices; using Dalamud.Game; using Dalamud.Hooking; +using Dalamud.Logging; using Dalamud.Utility.Signatures.Wrappers; - using Serilog; namespace Dalamud.Utility.Signatures; @@ -72,8 +72,9 @@ internal static class SignatureHelper } } + IntPtr ptr; var success = sig.ScanType == ScanType.Text - ? scanner.TryScanText(sig.Signature, out var ptr) + ? scanner.TryScanText(sig.Signature, out ptr) : scanner.TryGetStaticAddressFromSig(sig.Signature, out ptr); if (!success) { @@ -158,7 +159,7 @@ internal static class SignatureHelper continue; } - var hook = creator.Invoke(null, [ptr, detour, false]) as IDalamudHook; + var hook = creator.Invoke(null, new object?[] { ptr, detour, false }) as IDalamudHook; info.SetValue(self, hook); createdHooks.Add(hook); diff --git a/Dalamud/Utility/StringExtensions.cs b/Dalamud/Utility/StringExtensions.cs index 7f9975f82..c28aebab2 100644 --- a/Dalamud/Utility/StringExtensions.cs +++ b/Dalamud/Utility/StringExtensions.cs @@ -57,7 +57,7 @@ public static class StringExtensions /// <param name="input">The input string.</param> /// <param name="culture"><inheritdoc cref="string.ToLower(CultureInfo)" path="/param[@name='cultureInfo']"/></param> /// <returns>A new string with the first character converted to uppercase.</returns> - [return: NotNullIfNotNull(nameof(input))] + [return: NotNullIfNotNull("input")] public static string? FirstCharToUpper(this string? input, CultureInfo? culture = null) => string.IsNullOrWhiteSpace(input) ? input @@ -69,7 +69,7 @@ public static class StringExtensions /// <param name="input">The input string.</param> /// <param name="culture"><inheritdoc cref="string.ToLower(CultureInfo)" path="/param[@name='cultureInfo']"/></param> /// <returns>A new string with the first character converted to lowercase.</returns> - [return: NotNullIfNotNull(nameof(input))] + [return: NotNullIfNotNull("input")] public static string? FirstCharToLower(this string? input, CultureInfo? culture = null) => string.IsNullOrWhiteSpace(input) ? input diff --git a/Dalamud/Utility/TerraFxCom/ManagedIStream.cs b/Dalamud/Utility/TerraFxCom/ManagedIStream.cs index eb1997daf..caec65da2 100644 --- a/Dalamud/Utility/TerraFxCom/ManagedIStream.cs +++ b/Dalamud/Utility/TerraFxCom/ManagedIStream.cs @@ -57,60 +57,60 @@ internal sealed unsafe class ManagedIStream : IStream.Interface, IRefCountable static ManagedIStream? ToManagedObject(void* pThis) => GCHandle.FromIntPtr(((nint*)pThis)[1]).Target as ManagedIStream; - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static int QueryInterfaceStatic(IStream* pThis, Guid* riid, void** ppvObject) => ToManagedObject(pThis)?.QueryInterface(riid, ppvObject) ?? E.E_UNEXPECTED; - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static uint AddRefStatic(IStream* pThis) => (uint)(ToManagedObject(pThis)?.AddRef() ?? 0); - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static uint ReleaseStatic(IStream* pThis) => (uint)(ToManagedObject(pThis)?.Release() ?? 0); - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static int ReadStatic(IStream* pThis, void* pv, uint cb, uint* pcbRead) => ToManagedObject(pThis)?.Read(pv, cb, pcbRead) ?? E.E_UNEXPECTED; - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static int WriteStatic(IStream* pThis, void* pv, uint cb, uint* pcbWritten) => ToManagedObject(pThis)?.Write(pv, cb, pcbWritten) ?? E.E_UNEXPECTED; - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static int SeekStatic( IStream* pThis, LARGE_INTEGER dlibMove, uint dwOrigin, ULARGE_INTEGER* plibNewPosition) => ToManagedObject(pThis)?.Seek(dlibMove, dwOrigin, plibNewPosition) ?? E.E_UNEXPECTED; - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static int SetSizeStatic(IStream* pThis, ULARGE_INTEGER libNewSize) => ToManagedObject(pThis)?.SetSize(libNewSize) ?? E.E_UNEXPECTED; - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static int CopyToStatic( IStream* pThis, IStream* pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten) => ToManagedObject(pThis)?.CopyTo(pstm, cb, pcbRead, pcbWritten) ?? E.E_UNEXPECTED; - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static int CommitStatic(IStream* pThis, uint grfCommitFlags) => ToManagedObject(pThis)?.Commit(grfCommitFlags) ?? E.E_UNEXPECTED; - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static int RevertStatic(IStream* pThis) => ToManagedObject(pThis)?.Revert() ?? E.E_UNEXPECTED; - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static int LockRegionStatic(IStream* pThis, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, uint dwLockType) => ToManagedObject(pThis)?.LockRegion(libOffset, cb, dwLockType) ?? E.E_UNEXPECTED; - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static int UnlockRegionStatic( IStream* pThis, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, uint dwLockType) => ToManagedObject(pThis)?.UnlockRegion(libOffset, cb, dwLockType) ?? E.E_UNEXPECTED; - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static int StatStatic(IStream* pThis, STATSTG* pstatstg, uint grfStatFlag) => ToManagedObject(pThis)?.Stat(pstatstg, grfStatFlag) ?? E.E_UNEXPECTED; - [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + [UnmanagedCallersOnly] static int CloneStatic(IStream* pThis, IStream** ppstm) => ToManagedObject(pThis)?.Clone(ppstm) ?? E.E_UNEXPECTED; } diff --git a/Dalamud/Utility/TerraFxCom/TerraFxComInterfaceExtensions.cs b/Dalamud/Utility/TerraFxCom/TerraFxComInterfaceExtensions.cs index f952cba7e..f9252839f 100644 --- a/Dalamud/Utility/TerraFxCom/TerraFxComInterfaceExtensions.cs +++ b/Dalamud/Utility/TerraFxCom/TerraFxComInterfaceExtensions.cs @@ -51,28 +51,44 @@ internal static unsafe partial class TerraFxComInterfaceExtensions throw new ArgumentOutOfRangeException(nameof(mode), mode, null); } - grfMode |= access switch + switch (access) { - FileAccess.Read => STGM.STGM_READ, - FileAccess.Write => STGM.STGM_WRITE, - FileAccess.ReadWrite => (uint)STGM.STGM_READWRITE, - _ => throw new ArgumentOutOfRangeException(nameof(access), access, null), - }; + case FileAccess.Read: + grfMode |= STGM.STGM_READ; + break; + case FileAccess.Write: + grfMode |= STGM.STGM_WRITE; + break; + case FileAccess.ReadWrite: + grfMode |= STGM.STGM_READWRITE; + break; + default: + throw new ArgumentOutOfRangeException(nameof(access), access, null); + } - grfMode |= share switch + switch (share) { - FileShare.None => STGM.STGM_SHARE_EXCLUSIVE, - FileShare.Read => STGM.STGM_SHARE_DENY_WRITE, - FileShare.Write => STGM.STGM_SHARE_DENY_READ, - FileShare.ReadWrite => (uint)STGM.STGM_SHARE_DENY_NONE, - _ => throw new NotSupportedException($"Only ${FileShare.Read} and ${FileShare.Write} are supported."), - }; + case FileShare.None: + grfMode |= STGM.STGM_SHARE_EXCLUSIVE; + break; + case FileShare.Read: + grfMode |= STGM.STGM_SHARE_DENY_WRITE; + break; + case FileShare.Write: + grfMode |= STGM.STGM_SHARE_DENY_READ; + break; + case FileShare.ReadWrite: + grfMode |= STGM.STGM_SHARE_DENY_NONE; + break; + default: + throw new NotSupportedException($"Only ${FileShare.Read} and ${FileShare.Write} are supported."); + } using var stream = default(ComPtr<IStream>); fixed (char* pPath = path) { SHCreateStreamOnFileEx( - pPath, + (ushort*)pPath, grfMode, (uint)attributes, fCreate, @@ -99,7 +115,7 @@ internal static unsafe partial class TerraFxComInterfaceExtensions { fixed (char* pName = name) { - var option = new PROPBAG2 { pstrName = pName }; + var option = new PROPBAG2 { pstrName = (ushort*)pName }; return obj.Write(1, &option, &varValue); } } @@ -129,7 +145,7 @@ internal static unsafe partial class TerraFxComInterfaceExtensions try { fixed (char* pName = name) - return obj.SetMetadataByName(pName, &propVarValue); + return obj.SetMetadataByName((ushort*)pName, &propVarValue); } finally { @@ -149,7 +165,7 @@ internal static unsafe partial class TerraFxComInterfaceExtensions public static HRESULT RemoveMetadataByName(ref this IWICMetadataQueryWriter obj, string name) { fixed (char* pName = name) - return obj.RemoveMetadataByName(pName); + return obj.RemoveMetadataByName((ushort*)pName); } [LibraryImport("propsys.dll")] diff --git a/Dalamud/Utility/Timing/Timings.cs b/Dalamud/Utility/Timing/Timings.cs index 563221fb9..e2c00461c 100644 --- a/Dalamud/Utility/Timing/Timings.cs +++ b/Dalamud/Utility/Timing/Timings.cs @@ -19,12 +19,12 @@ public static class Timings /// <summary> /// All concluded timings. /// </summary> - internal static readonly SortedList<TimingHandle, TimingHandle> AllTimings = []; + internal static readonly SortedList<TimingHandle, TimingHandle> AllTimings = new(); /// <summary> /// List of all timing events. /// </summary> - internal static readonly List<TimingEvent> Events = []; + internal static readonly List<TimingEvent> Events = new(); private static readonly AsyncLocal<Tuple<int?, List<TimingHandle>>> TaskTimingHandleStorage = new(); @@ -36,7 +36,7 @@ public static class Timings get { if (TaskTimingHandleStorage.Value == null || TaskTimingHandleStorage.Value.Item1 != Task.CurrentId) - TaskTimingHandleStorage.Value = Tuple.Create<int?, List<TimingHandle>>(Task.CurrentId, []); + TaskTimingHandleStorage.Value = Tuple.Create<int?, List<TimingHandle>>(Task.CurrentId, new()); return TaskTimingHandleStorage.Value!.Item2!; } set => TaskTimingHandleStorage.Value = Tuple.Create(Task.CurrentId, value); @@ -53,7 +53,7 @@ public static class Timings var outerTimingHandle = TaskTimingHandles; return () => { - var res = default(T); + T res = default(T); var prev = TaskTimingHandles; TaskTimingHandles = outerTimingHandle; try diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index 72db5cfc6..19610ef64 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -68,10 +68,106 @@ public static partial class Util ]; private static readonly Type GenericSpanType = typeof(Span<>); + private static string? scmVersionInternal; + private static string? gitHashInternal; + private static string? gitHashClientStructsInternal; + private static string? branchInternal; private static ulong moduleStartAddr; private static ulong moduleEndAddr; + /// <summary> + /// Gets the Dalamud version. + /// </summary> + [Api13ToDo("Remove. Make both versions here internal. Add an API somewhere.")] + public static string AssemblyVersion { get; } = + Assembly.GetAssembly(typeof(ChatHandlers))!.GetName().Version!.ToString(); + + /// <summary> + /// Gets the Dalamud version. + /// </summary> + internal static Version AssemblyVersionParsed { get; } = + Assembly.GetAssembly(typeof(ChatHandlers))!.GetName().Version!; + + /// <summary> + /// Gets the SCM Version from the assembly, or null if it cannot be found. This method will generally return + /// the <c>git describe</c> output for this build, which will be a raw version if this is a stable build or an + /// appropriately-annotated version if this is *not* stable. Local builds will return a `Local Build` text string. + /// </summary> + /// <returns>The SCM version of the assembly.</returns> + public static string GetScmVersion() + { + if (scmVersionInternal != null) return scmVersionInternal; + + var asm = typeof(Util).Assembly; + var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>(); + + return scmVersionInternal = attrs.First(a => a.Key == "SCMVersion").Value + ?? asm.GetName().Version!.ToString(); + } + + /// <summary> + /// Gets the git commit hash value from the assembly or null if it cannot be found. Will be null for Debug builds, + /// and will be suffixed with `-dirty` if in release with pending changes. + /// </summary> + /// <returns>The git hash of the assembly.</returns> + public static string? GetGitHash() + { + if (gitHashInternal != null) + return gitHashInternal; + + var asm = typeof(Util).Assembly; + var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>(); + + return gitHashInternal = attrs.FirstOrDefault(a => a.Key == "GitHash")?.Value ?? "N/A"; + } + + /// <summary> + /// Gets the git hash value from the assembly or null if it cannot be found. + /// </summary> + /// <returns>The git hash of the assembly.</returns> + public static string? GetGitHashClientStructs() + { + if (gitHashClientStructsInternal != null) + return gitHashClientStructsInternal; + + var asm = typeof(Util).Assembly; + var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>(); + + gitHashClientStructsInternal = attrs.First(a => a.Key == "GitHashClientStructs").Value; + + return gitHashClientStructsInternal; + } + + /// <summary> + /// Gets the Git branch name this version of Dalamud was built from, or null, if this is a Debug build. + /// </summary> + /// <returns>The branch name.</returns> + public static string? GetGitBranch() + { + if (branchInternal != null) + return branchInternal; + + var asm = typeof(Util).Assembly; + var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>(); + + var gitBranch = attrs.FirstOrDefault(a => a.Key == "GitBranch")?.Value; + if (gitBranch == null) + return null; + + return branchInternal = gitBranch; + } + + /// <summary> + /// Gets the active Dalamud track, if this instance was launched through XIVLauncher and used a version + /// downloaded from webservices. + /// </summary> + /// <returns>The name of the track, or null.</returns> + internal static string? GetActiveTrack() + { + return Environment.GetEnvironmentVariable("DALAMUD_BRANCH"); + } + /// <inheritdoc cref="DescribeAddress(nint)"/> public static unsafe string DescribeAddress(void* p) => DescribeAddress((nint)p); @@ -367,7 +463,7 @@ public static partial class Util /// <returns>Human readable version.</returns> public static string FormatBytes(long bytes) { - string[] suffix = ["B", "KB", "MB", "GB", "TB"]; + string[] suffix = { "B", "KB", "MB", "GB", "TB" }; int i; double dblSByte = bytes; for (i = 0; i < suffix.Length && bytes >= 1024; i++, bytes /= 1024) @@ -762,7 +858,7 @@ public static partial class Util var sizeWithTerminators = pathBytesSize + (pathBytes.Length * 2); var dropFilesSize = sizeof(DROPFILES); - var hGlobal = Win32_PInvoke.GlobalAlloc( + var hGlobal = Win32_PInvoke.GlobalAlloc_SafeHandle( GLOBAL_ALLOC_FLAGS.GHND, // struct size + size of encoded strings + null terminator for each // string + two null terminators for end of list @@ -800,11 +896,12 @@ public static partial class Util { Win32_PInvoke.SetClipboardData( (uint)CLIPBOARD_FORMAT.CF_HDROP, - (Windows.Win32.Foundation.HANDLE)hGlobal.Value); + hGlobal); Win32_PInvoke.CloseClipboard(); return true; } + hGlobal.Dispose(); return false; } @@ -823,7 +920,7 @@ public static partial class Util MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, null, - [typeof(object), typeof(IList<string>), typeof(ulong)], + new[] { typeof(object), typeof(IList<string>), typeof(ulong) }, obj.GetType(), true); @@ -850,7 +947,7 @@ public static partial class Util ilg.Emit(OpCodes.Call, mm); ilg.Emit(OpCodes.Ret); - dm.Invoke(null, [obj, path, addr]); + dm.Invoke(null, new[] { obj, path, addr }); } #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type @@ -1029,8 +1126,8 @@ public static partial class Util foreach (var f in obj.GetType() .GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance)) { - var fixedBuffer = f.GetCustomAttribute<FixedBufferAttribute>(); - var offset = f.GetCustomAttribute<FieldOffsetAttribute>(); + var fixedBuffer = (FixedBufferAttribute)f.GetCustomAttribute(typeof(FixedBufferAttribute)); + var offset = (FieldOffsetAttribute)f.GetCustomAttribute(typeof(FieldOffsetAttribute)); if (fixedBuffer != null) { diff --git a/Dalamud/Utility/VectorExtensions.cs b/Dalamud/Utility/VectorExtensions.cs new file mode 100644 index 000000000..f617c8420 --- /dev/null +++ b/Dalamud/Utility/VectorExtensions.cs @@ -0,0 +1,51 @@ +using System.Numerics; + +namespace Dalamud.Utility; + +/// <summary> +/// Extension methods for System.Numerics.VectorN and SharpDX.VectorN. +/// </summary> +public static class VectorExtensions +{ + /// <summary> + /// Converts a SharpDX vector to System.Numerics. + /// </summary> + /// <param name="vec">Vector to convert.</param> + /// <returns>A converted vector.</returns> + public static Vector2 ToSystem(this SharpDX.Vector2 vec) => new(x: vec.X, y: vec.Y); + + /// <summary> + /// Converts a SharpDX vector to System.Numerics. + /// </summary> + /// <param name="vec">Vector to convert.</param> + /// <returns>A converted vector.</returns> + public static Vector3 ToSystem(this SharpDX.Vector3 vec) => new(x: vec.X, y: vec.Y, z: vec.Z); + + /// <summary> + /// Converts a SharpDX vector to System.Numerics. + /// </summary> + /// <param name="vec">Vector to convert.</param> + /// <returns>A converted vector.</returns> + public static Vector4 ToSystem(this SharpDX.Vector4 vec) => new(x: vec.X, y: vec.Y, z: vec.Z, w: vec.W); + + /// <summary> + /// Converts a System.Numerics vector to SharpDX. + /// </summary> + /// <param name="vec">Vector to convert.</param> + /// <returns>A converted vector.</returns> + public static SharpDX.Vector2 ToSharpDX(this Vector2 vec) => new(x: vec.X, y: vec.Y); + + /// <summary> + /// Converts a System.Numerics vector to SharpDX. + /// </summary> + /// <param name="vec">Vector to convert.</param> + /// <returns>A converted vector.</returns> + public static SharpDX.Vector3 ToSharpDX(this Vector3 vec) => new(x: vec.X, y: vec.Y, z: vec.Z); + + /// <summary> + /// Converts a System.Numerics vector to SharpDX. + /// </summary> + /// <param name="vec">Vector to convert.</param> + /// <returns>A converted vector.</returns> + public static SharpDX.Vector4 ToSharpDX(this Vector4 vec) => new(x: vec.X, y: vec.Y, z: vec.Z, w: vec.W); +} diff --git a/Dalamud/Utility/Versioning.cs b/Dalamud/Utility/Versioning.cs deleted file mode 100644 index d3b30b834..000000000 --- a/Dalamud/Utility/Versioning.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System.Linq; -using System.Reflection; - -namespace Dalamud.Utility; - -/// <summary> -/// Helpers to access Dalamud versioning information. -/// </summary> -internal static class Versioning -{ - private static string? scmVersionInternal; - private static string? gitHashInternal; - private static string? gitHashClientStructsInternal; - private static string? branchInternal; - - /// <summary> - /// Gets the Dalamud version. - /// </summary> - /// <returns>The raw Dalamud assembly version.</returns> - internal static string GetAssemblyVersion() => - Assembly.GetAssembly(typeof(Versioning))!.GetName().Version!.ToString(); - - /// <summary> - /// Gets the Dalamud version. - /// </summary> - /// <returns>The parsed Dalamud assembly version.</returns> - internal static Version GetAssemblyVersionParsed() => - Assembly.GetAssembly(typeof(Versioning))!.GetName().Version!; - - /// <summary> - /// Gets the SCM Version from the assembly, or null if it cannot be found. This method will generally return - /// the <c>git describe</c> output for this build, which will be a raw version if this is a stable build or an - /// appropriately-annotated version if this is *not* stable. Local builds will return a `Local Build` text string. - /// </summary> - /// <returns>The SCM version of the assembly.</returns> - internal static string GetScmVersion() - { - if (scmVersionInternal != null) return scmVersionInternal; - - var asm = typeof(Util).Assembly; - var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>(); - - return scmVersionInternal = attrs.First(a => a.Key == "SCMVersion").Value - ?? asm.GetName().Version!.ToString(); - } - - /// <summary> - /// Gets the git commit hash value from the assembly or null if it cannot be found. Will be null for Debug builds, - /// and will be suffixed with `-dirty` if in release with pending changes. - /// </summary> - /// <returns>The git hash of the assembly.</returns> - internal static string? GetGitHash() - { - if (gitHashInternal != null) - return gitHashInternal; - - var asm = typeof(Util).Assembly; - var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>(); - - return gitHashInternal = attrs.FirstOrDefault(a => a.Key == "GitHash")?.Value ?? "N/A"; - } - - /// <summary> - /// Gets the git hash value from the assembly or null if it cannot be found. - /// </summary> - /// <returns>The git hash of the assembly.</returns> - internal static string? GetGitHashClientStructs() - { - if (gitHashClientStructsInternal != null) - return gitHashClientStructsInternal; - - var asm = typeof(Util).Assembly; - var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>(); - - gitHashClientStructsInternal = attrs.First(a => a.Key == "GitHashClientStructs").Value; - - return gitHashClientStructsInternal; - } - - /// <summary> - /// Gets the Git branch name this version of Dalamud was built from, or null, if this is a Debug build. - /// </summary> - /// <returns>The branch name.</returns> - internal static string? GetGitBranch() - { - if (branchInternal != null) - return branchInternal; - - var asm = typeof(Util).Assembly; - var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>(); - - var gitBranch = attrs.FirstOrDefault(a => a.Key == "GitBranch")?.Value; - if (gitBranch == null) - return null; - - return branchInternal = gitBranch; - } - - /// <summary> - /// Gets the active Dalamud track, if this instance was launched through XIVLauncher and used a version - /// downloaded from webservices. - /// </summary> - /// <returns>The name of the track, or null.</returns> - internal static string? GetActiveTrack() - { - return Environment.GetEnvironmentVariable("DALAMUD_BRANCH"); - } -} diff --git a/Dalamud/Utility/WeakConcurrentCollection.cs b/Dalamud/Utility/WeakConcurrentCollection.cs index c23ebac9a..a3bc04651 100644 --- a/Dalamud/Utility/WeakConcurrentCollection.cs +++ b/Dalamud/Utility/WeakConcurrentCollection.cs @@ -1,4 +1,4 @@ -using System.Collections; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; @@ -11,7 +11,7 @@ namespace Dalamud.Utility; /// <typeparam name="T">The type of object that we're tracking.</typeparam> public class WeakConcurrentCollection<T> : ICollection<T> where T : class { - private readonly ConditionalWeakTable<T, object> cwt = []; + private readonly ConditionalWeakTable<T, object> cwt = new(); /// <inheritdoc/> public int Count => this.cwt.Count(); diff --git a/DalamudCrashHandler/DalamudCrashHandler.cpp b/DalamudCrashHandler/DalamudCrashHandler.cpp index f883ba55b..ec7115ffd 100644 --- a/DalamudCrashHandler/DalamudCrashHandler.cpp +++ b/DalamudCrashHandler/DalamudCrashHandler.cpp @@ -19,7 +19,6 @@ #include <comdef.h> #include <CommCtrl.h> #include <DbgHelp.h> -#include <intrin.h> #include <minidumpapiset.h> #include <PathCch.h> #include <Psapi.h> @@ -28,9 +27,6 @@ #include <ShObjIdl.h> #include <shlobj_core.h> -#include <dxgi.h> -#pragma comment(lib, "dxgi.lib") - #pragma comment(lib, "comctl32.lib") #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") @@ -123,7 +119,7 @@ std::wstring describe_module(const std::filesystem::path& path) { return std::format(L"<error: GetFileVersionInfoSizeW#2 returned {}>", GetLastError()); UINT size = 0; - + std::wstring version = L"v?.?.?.?"; if (LPVOID lpBuffer; VerQueryValueW(block.data(), L"\\", &lpBuffer, &size)) { const auto& v = *static_cast<const VS_FIXEDFILEINFO*>(lpBuffer); @@ -180,7 +176,7 @@ const std::map<HMODULE, size_t>& get_remote_modules() { std::vector<HMODULE> buf(8192); for (size_t i = 0; i < 64; i++) { if (DWORD needed; !EnumProcessModules(g_hProcess, &buf[0], static_cast<DWORD>(std::span(buf).size_bytes()), &needed)) { - std::cerr << std::format("EnumProcessModules error: 0x{:x}", GetLastError()) << std::endl; + std::cerr << std::format("EnumProcessModules error: 0x{:x}", GetLastError()) << std::endl; break; } else if (needed > std::span(buf).size_bytes()) { buf.resize(needed / sizeof(HMODULE) + 16); @@ -205,7 +201,7 @@ const std::map<HMODULE, size_t>& get_remote_modules() { data[hModule] = nth64.OptionalHeader.SizeOfImage; } - + return data; }(); @@ -296,43 +292,35 @@ std::wstring to_address_string(const DWORD64 address, const bool try_ptrderef = void print_exception_info(HANDLE hThread, const EXCEPTION_POINTERS& ex, const CONTEXT& ctx, std::wostringstream& log) { std::vector<EXCEPTION_RECORD> exRecs; - if (ex.ExceptionRecord) - { + if (ex.ExceptionRecord) { size_t rec_index = 0; size_t read; - + exRecs.emplace_back(); for (auto pRemoteExRec = ex.ExceptionRecord; - pRemoteExRec && rec_index < 64; - rec_index++) - { - exRecs.emplace_back(); - - if (!ReadProcessMemory(g_hProcess, pRemoteExRec, &exRecs.back(), sizeof exRecs.back(), &read) - || read < offsetof(EXCEPTION_RECORD, ExceptionInformation) - || read < static_cast<size_t>(reinterpret_cast<const char*>(&exRecs.back().ExceptionInformation[exRecs. - back().NumberParameters]) - reinterpret_cast<const char*>(&exRecs.back()))) - { - exRecs.pop_back(); - break; - } + pRemoteExRec + && rec_index < 64 + && ReadProcessMemory(g_hProcess, pRemoteExRec, &exRecs.back(), sizeof exRecs.back(), &read) + && read >= offsetof(EXCEPTION_RECORD, ExceptionInformation) + && read >= static_cast<size_t>(reinterpret_cast<const char*>(&exRecs.back().ExceptionInformation[exRecs.back().NumberParameters]) - reinterpret_cast<const char*>(&exRecs.back())); + rec_index++) { log << std::format(L"\nException Info #{}\n", rec_index); log << std::format(L"Address: {:X}\n", exRecs.back().ExceptionCode); log << std::format(L"Flags: {:X}\n", exRecs.back().ExceptionFlags); log << std::format(L"Address: {:X}\n", reinterpret_cast<size_t>(exRecs.back().ExceptionAddress)); - if (exRecs.back().NumberParameters) - { - log << L"Parameters: "; - for (DWORD i = 0; i < exRecs.back().NumberParameters; ++i) - { - if (i != 0) - log << L", "; - log << std::format(L"{:X}", exRecs.back().ExceptionInformation[i]); - } + if (!exRecs.back().NumberParameters) + continue; + log << L"Parameters: "; + for (DWORD i = 0; i < exRecs.back().NumberParameters; ++i) { + if (i != 0) + log << L", "; + log << std::format(L"{:X}", exRecs.back().ExceptionInformation[i]); } pRemoteExRec = exRecs.back().ExceptionRecord; + exRecs.emplace_back(); } + exRecs.pop_back(); } log << L"\nCall Stack\n{"; @@ -422,7 +410,7 @@ void print_exception_info_extended(const EXCEPTION_POINTERS& ex, const CONTEXT& std::wstring escape_shell_arg(const std::wstring& arg) { // https://docs.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way - + std::wstring res; if (!arg.empty() && arg.find_first_of(L" \t\n\v\"") == std::wstring::npos) { res.append(arg); @@ -473,39 +461,12 @@ void open_folder_and_select_items(HWND hwndOpener, const std::wstring& path) { ILFree(piid); } -std::vector<IDXGIAdapter1*> enum_dxgi_adapters() -{ - std::vector<IDXGIAdapter1*> vAdapters; - - IDXGIFactory1* pFactory = NULL; - if (FAILED(CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&pFactory))) - { - return vAdapters; - } - - IDXGIAdapter1* pAdapter; - for (UINT i = 0; - pFactory->EnumAdapters1(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; - ++i) - { - vAdapters.push_back(pAdapter); - } - - if (pFactory) - { - pFactory->Release(); - } - - return vAdapters; -} - void export_tspack(HWND hWndParent, const std::filesystem::path& logDir, const std::string& crashLog, const std::string& troubleshootingPackData) { static const char* SourceLogFiles[] = { "output.log", // XIVLauncher for Windows "launcher.log", // XIVLauncher.Core for [mostly] Linux "patcher.log", "dalamud.log", - "dalamud.troubleshooting.json", "dalamud.injector.log", "dalamud.boot.log", "aria.log", @@ -543,7 +504,7 @@ void export_tspack(HWND hWndParent, const std::filesystem::path& logDir, const s filePath.emplace(pFilePath); std::fstream fileStream(*filePath, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc); - + mz_zip_archive zipa{}; zipa.m_pIO_opaque = &fileStream; zipa.m_pRead = [](void* pOpaque, mz_uint64 file_ofs, void* pBuf, size_t n) -> size_t { @@ -605,7 +566,7 @@ void export_tspack(HWND hWndParent, const std::filesystem::path& logDir, const s const auto hLogFile = CreateFileW(logFilePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr); if (hLogFile == INVALID_HANDLE_VALUE) throw_last_error(std::format("indiv. log file: CreateFileW({})", ws_to_u8(logFilePath.wstring()))); - + std::unique_ptr<void, decltype(&CloseHandle)> hLogFileClose(hLogFile, &CloseHandle); LARGE_INTEGER size, baseOffset{}; @@ -720,60 +681,6 @@ void restart_game_using_injector(int nRadioButton, const std::vector<std::wstrin } } -void get_cpu_info(wchar_t *vendor, wchar_t *brand) -{ - // Gotten and reformatted to not include all data as listed at https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=msvc-170#example - - // int cpuInfo[4] = {-1}; - std::array<int, 4> cpui; - int nIds_; - int nExIds_; - std::vector<std::array<int, 4>> data_; - std::vector<std::array<int, 4>> extdata_; - size_t convertedChars = 0; - - // Calling __cpuid with 0x0 as the function_id argument - // gets the number of the highest valid function ID. - __cpuid(cpui.data(), 0); - nIds_ = cpui[0]; - - for (int i = 0; i <= nIds_; ++i) - { - __cpuidex(cpui.data(), i, 0); - data_.push_back(cpui); - } - - // Capture vendor string - char vendorA[0x20]; - memset(vendorA, 0, sizeof(vendorA)); - *reinterpret_cast<int *>(vendorA) = data_[0][1]; - *reinterpret_cast<int *>(vendorA + 4) = data_[0][3]; - *reinterpret_cast<int *>(vendorA + 8) = data_[0][2]; - mbstowcs_s(&convertedChars, vendor, 0x20, vendorA, _TRUNCATE); - - // Calling __cpuid with 0x80000000 as the function_id argument - // gets the number of the highest valid extended ID. - __cpuid(cpui.data(), 0x80000000); - nExIds_ = cpui[0]; - - for (int i = 0x80000000; i <= nExIds_; ++i) - { - __cpuidex(cpui.data(), i, 0); - extdata_.push_back(cpui); - } - - // Interpret CPU brand string if reported - if (nExIds_ >= 0x80000004) - { - char brandA[0x40]; - memset(brandA, 0, sizeof(brandA)); - memcpy(brandA, extdata_[2].data(), sizeof(cpui)); - memcpy(brandA + 16, extdata_[3].data(), sizeof(cpui)); - memcpy(brandA + 32, extdata_[4].data(), sizeof(cpui)); - mbstowcs_s(&convertedChars, brand, 0x40, brandA, _TRUNCATE); - } -} - int main() { enum crash_handler_special_exit_codes { UnknownError = -99, @@ -788,7 +695,7 @@ int main() { // IFileSaveDialog only works on STA CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - + std::vector<std::wstring> args; if (int argc = 0; const auto argv = CommandLineToArgvW(GetCommandLineW(), &argc)) { for (auto i = 0; i < argc; i++) @@ -916,14 +823,14 @@ int main() { hr = pOleWindow->GetWindow(&hwndProgressDialog); if (SUCCEEDED(hr)) { - SetWindowPos(hwndProgressDialog, HWND_TOPMOST, 0, 0, 0, 0, + SetWindowPos(hwndProgressDialog, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); SetForegroundWindow(hwndProgressDialog); } - + pOleWindow->Release(); } - + } else { std::cerr << "Failed to create progress window" << std::endl; @@ -945,14 +852,14 @@ int main() { https://github.com/sumatrapdfreader/sumatrapdf/blob/master/src/utils/DbgHelpDyn.cpp */ - + if (g_bSymbolsAvailable) { SymRefreshModuleList(g_hProcess); } else if(!assetDir.empty()) { auto symbol_search_path = std::format(L".;{}", (assetDir / "UIRes" / "pdb").wstring()); - + g_bSymbolsAvailable = SymInitializeW(g_hProcess, symbol_search_path.c_str(), true); std::wcout << std::format(L"Init symbols with PDB at {}", symbol_search_path) << std::endl; @@ -963,12 +870,12 @@ int main() { g_bSymbolsAvailable = SymInitializeW(g_hProcess, nullptr, true); std::cout << "Init symbols without PDB" << std::endl; } - + if (!g_bSymbolsAvailable) { std::wcerr << std::format(L"SymInitialize error: 0x{:x}", GetLastError()) << std::endl; } - if (pProgressDialog) + if (pProgressDialog) pProgressDialog->SetLine(3, L"Reading troubleshooting data", FALSE, NULL); std::wstring stackTrace(exinfo.dwStackTraceLength, L'\0'); @@ -1023,26 +930,13 @@ int main() { } while (false); } - const bool is_external_event = exinfo.ExceptionRecord.ExceptionCode == CUSTOM_EXCEPTION_EXTERNAL_EVENT; - std::wostringstream log; - wchar_t vendor[0x20]; - wchar_t brand[0x40]; - get_cpu_info(vendor, brand); - - if (!is_external_event) - { - log << std::format(L"Unhandled native exception occurred at {}", to_address_string(exinfo.ContextRecord.Rip, false)) << std::endl; - log << std::format(L"Code: {:X}", exinfo.ExceptionRecord.ExceptionCode) << std::endl; - } - else - { - log << L"CLR error occurred" << std::endl; - } + log << std::format(L"Unhandled native exception occurred at {}", to_address_string(exinfo.ContextRecord.Rip, false)) << std::endl; + log << std::format(L"Code: {:X}", exinfo.ExceptionRecord.ExceptionCode) << std::endl; if (shutup) log << L"======= Crash handler was globally muted(shutdown?) =======" << std::endl; - + if (dumpPath.empty()) log << L"Dump skipped" << std::endl; else if (dumpError.empty()) @@ -1050,33 +944,14 @@ int main() { else log << std::format(L"Dump error: {}", dumpError) << std::endl; log << std::format(L"System Time: {0:%F} {0:%T} {0:%Ez}", std::chrono::system_clock::now()) << std::endl; - log << std::format(L"CPU Vendor: {}", vendor) << std::endl; - log << std::format(L"CPU Brand: {}", brand) << std::endl; - - for (IDXGIAdapter1* adapter : enum_dxgi_adapters()) { - DXGI_ADAPTER_DESC1 adapterDescription{}; - adapter->GetDesc1(&adapterDescription); - log << std::format(L"GPU Desc: {}", adapterDescription.Description) << std::endl; - } - log << L"\n" << stackTrace << std::endl; if (pProgressDialog) pProgressDialog->SetLine(3, L"Refreshing Module List", FALSE, NULL); - std::wstring window_log_str; - - // Cut the log here for external events, the rest is unreadable and doesn't matter since we can't get - // symbols for mixed-mode stacks yet. - if (is_external_event) - window_log_str = log.str(); - SymRefreshModuleList(GetCurrentProcess()); print_exception_info(exinfo.hThreadHandle, exinfo.ExceptionPointers, exinfo.ContextRecord, log); - - if (!is_external_event) - window_log_str = log.str(); - + const auto window_log_str = log.str(); print_exception_info_extended(exinfo.ExceptionPointers, exinfo.ContextRecord, log); std::wofstream(logPath) << log.str(); @@ -1111,8 +986,8 @@ int main() { config.pButtons = buttons; config.cButtons = ARRAYSIZE(buttons); config.nDefaultButton = IdButtonRestart; - config.pszExpandedControlText = L"Hide further information"; - config.pszCollapsedControlText = L"Further information for developers"; + config.pszExpandedControlText = L"Hide stack trace"; + config.pszCollapsedControlText = L"Stack trace for plugin developers"; config.pszExpandedInformation = window_log_str.c_str(); config.pszWindowTitle = L"Dalamud Crash Handler"; config.pRadioButtons = radios; @@ -1128,7 +1003,7 @@ int main() { R"aa(<a href="help">Help</a> | <a href="logdir">Open log directory</a> | <a href="logfile">Open log file</a>)aa" ); #endif - + // Can't do this, xiv stops pumping messages here //config.hwndParent = FindWindowA("FFXIVGAME", NULL); @@ -1181,13 +1056,13 @@ int main() { return (*reinterpret_cast<decltype(callback)*>(dwRefData))(hwnd, uNotification, wParam, lParam); }; config.lpCallbackData = reinterpret_cast<LONG_PTR>(&callback); - + if (pProgressDialog) { pProgressDialog->StopProgressDialog(); pProgressDialog->Release(); pProgressDialog = NULL; } - + const auto kill_game = [&] { TerminateProcess(g_hProcess, exinfo.ExceptionRecord.ExceptionCode); }; if (shutup) { diff --git a/Directory.Build.props b/Directory.Build.props index 8a8df22d7..4ed87c809 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,16 +2,10 @@ <Project> <PropertyGroup Label="Target"> - <TargetFramework>net10.0-windows</TargetFramework> + <TargetFramework>net9.0-windows</TargetFramework> <PlatformTarget>x64</PlatformTarget> <Platforms>x64</Platforms> - - <!-- preview, as docfx is late at upgrading Roslyn versions --> - <LangVersion>preview</LangVersion> - - <!-- Disable Intel CET. Causes crashes on unpatched Windows 10 systems. --> - <!-- https://github.com/dotnet/runtime/issues/108589 --> - <CETCompat>false</CETCompat> + <LangVersion>13.0</LangVersion> </PropertyGroup> <!-- Code analysis settings for all Dalamud projects. --> diff --git a/Directory.Packages.props b/Directory.Packages.props index 2a8c52dc4..a1cef517e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,60 +1,65 @@ <Project> - <PropertyGroup> - <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> - <CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled> - </PropertyGroup> + <PropertyGroup> + <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> + <CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled> + </PropertyGroup> - <ItemGroup> - <!-- Analyzers --> - <PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="4.14.0" /> - <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" /> - <PackageVersion Include="JetBrains.Annotations" Version="2025.2.4" /> + <ItemGroup> + <!-- Analyzers --> + <PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"/> + <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556"/> + <PackageVersion Include="JetBrains.Annotations" Version="2025.2.2"/> - <!-- Misc Libraries --> - <PackageVersion Include="BitFaster.Caching" Version="2.5.4" /> - <PackageVersion Include="CheapLoc" Version="1.1.8" /> - <PackageVersion Include="MinSharp" Version="1.0.4" /> - <PackageVersion Include="Newtonsoft.Json" Version="13.0.4" /> - <PackageVersion Include="Lumina" Version="7.1.0" /> - <PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="10.0.0" /> - <PackageVersion Include="System.Reactive" Version="6.1.0" /> - <PackageVersion Include="System.Reflection.MetadataLoadContext" Version="10.0.0" /> - <PackageVersion Include="DotNet.ReproducibleBuilds" Version="1.2.39" /> - <PackageVersion Include="sqlite-net-pcl" Version="1.9.172" /> + <!-- Misc Libraries --> + <PackageVersion Include="BitFaster.Caching" Version="2.4.1"/> + <PackageVersion Include="CheapLoc" Version="1.1.8"/> + <PackageVersion Include="MinSharp" Version="1.0.4"/> + <PackageVersion Include="Newtonsoft.Json" Version="13.0.3"/> + <PackageVersion Include="Lumina" Version="6.5.1"/> + <PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="8.0.7"/> + <PackageVersion Include="System.Collections.Immutable" Version="8.0.0"/> + <PackageVersion Include="System.Drawing.Common" Version="8.0.0"/> + <PackageVersion Include="System.Reactive" Version="5.0.0"/> + <PackageVersion Include="System.Reflection.MetadataLoadContext" Version="8.0.0"/> + <PackageVersion Include="System.Resources.Extensions" Version="8.0.0"/> + <PackageVersion Include="DotNet.ReproducibleBuilds" Version="1.2.39"/> + <PackageVersion Include="sqlite-net-pcl" Version="1.8.116"/> - <!-- DirectX / Win32 --> - <PackageVersion Include="TerraFX.Interop.Windows" Version="10.0.26100.5" /> - <PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.259" /> + <!-- DirectX / Win32 --> + <PackageVersion Include="TerraFX.Interop.Windows" Version="10.0.22621.2"/> + <PackageVersion Include="SharpDX.Direct3D11" Version="4.2.0"/> + <PackageVersion Include="SharpDX.Mathematics" Version="4.2.0"/> + <PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.183"/> - <!-- Logging --> - <PackageVersion Include="Serilog" Version="4.3.0" /> - <PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" /> - <PackageVersion Include="Serilog.Sinks.Console" Version="6.1.1" /> - <PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" /> + <!-- Logging --> + <PackageVersion Include="Serilog" Version="4.0.2"/> + <PackageVersion Include="Serilog.Sinks.Async" Version="2.0.0"/> + <PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0"/> + <PackageVersion Include="Serilog.Sinks.File" Version="6.0.0"/> - <!-- Injector Utilities --> - <!-- Take care: must be pinned to version used in goatcorp.Reloaded.Hooks, otherwise we are trampling the injector's version when building Dalamud --> - <PackageVersion Include="Iced" Version="1.17.0" /> - <PackageVersion Include="PeNet" Version="5.1.0" /> + <!-- Injector Utilities --> + <PackageVersion Include="Iced" Version="1.17.0"/> + <PackageVersion Include="PeNet" Version="2.6.4"/> - <!-- HexaGen --> - <PackageVersion Include="HexaGen.Runtime" Version="1.1.20" /> + <!-- HexaGen --> + <PackageVersion Include="HexaGen.Runtime" Version="1.1.20"/> - <!-- Reloaded --> - <PackageVersion Include="goatcorp.Reloaded.Hooks" Version="4.2.0-goatcorp8" /> - <PackageVersion Include="Reloaded.Memory" Version="7.0.0" /> - <PackageVersion Include="Reloaded.Memory.Buffers" Version="2.0.0" /> + <!-- Reloaded --> + <PackageVersion Include="goatcorp.Reloaded.Hooks" Version="4.2.0-goatcorp7"/> + <PackageVersion Include="goatcorp.Reloaded.Assembler" Version="1.0.14-goatcorp5"/> + <PackageVersion Include="Reloaded.Memory" Version="7.0.0"/> + <PackageVersion Include="Reloaded.Memory.Buffers" Version="2.0.0"/> - <!-- Unit Testing --> - <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" /> - <PackageVersion Include="xunit" Version="2.9.3" /> - <PackageVersion Include="xunit.abstractions" Version="2.0.3" /> - <PackageVersion Include="xunit.analyzers" Version="1.26.0" /> - <PackageVersion Include="xunit.assert" Version="2.9.3" /> - <PackageVersion Include="xunit.core" Version="2.9.3" /> - <PackageVersion Include="xunit.extensibility.core" Version="2.9.3" /> - <PackageVersion Include="xunit.extensibility.execution" Version="2.9.3" /> - <PackageVersion Include="xunit.runner.console" Version="2.9.3" /> - <PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" /> - </ItemGroup> + <!-- Unit Testing --> + <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="16.10.0"/> + <PackageVersion Include="xunit" Version="2.4.1"/> + <PackageVersion Include="xunit.abstractions" Version="2.0.3"/> + <PackageVersion Include="xunit.analyzers" Version="0.10.0"/> + <PackageVersion Include="xunit.assert" Version="2.4.1"/> + <PackageVersion Include="xunit.core" Version="2.4.1"/> + <PackageVersion Include="xunit.extensibility.core" Version="2.4.1"/> + <PackageVersion Include="xunit.extensibility.execution" Version="2.4.1"/> + <PackageVersion Include="xunit.runner.console" Version="2.4.1"/> + <PackageVersion Include="xunit.runner.visualstudio" Version="2.4.3"/> + </ItemGroup> </Project> diff --git a/build/DalamudBuild.cs b/build/DalamudBuild.cs index 1a189f2c7..d374c79f8 100644 --- a/build/DalamudBuild.cs +++ b/build/DalamudBuild.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using Nuke.Common; using Nuke.Common.Execution; using Nuke.Common.Git; @@ -41,7 +42,10 @@ public class DalamudBuild : NukeBuild AbsolutePath InjectorProjectDir => RootDirectory / "Dalamud.Injector"; AbsolutePath InjectorProjectFile => InjectorProjectDir / "Dalamud.Injector.csproj"; - + + AbsolutePath InjectorBootProjectDir => RootDirectory / "Dalamud.Injector.Boot"; + AbsolutePath InjectorBootProjectFile => InjectorBootProjectDir / "Dalamud.Injector.Boot.vcxproj"; + AbsolutePath TestProjectDir => RootDirectory / "Dalamud.Test"; AbsolutePath TestProjectFile => TestProjectDir / "Dalamud.Test.csproj"; @@ -127,7 +131,7 @@ public class DalamudBuild : NukeBuild if (IsCIBuild) { s = s - .SetProcessAdditionalArguments("/clp:NoSummary"); // Disable MSBuild summary on CI builds + .SetProcessArgumentConfigurator(a => a.Add("/clp:NoSummary")); // Disable MSBuild summary on CI builds } // We need to emit compiler generated files for the docs build, since docfx can't run generators directly // TODO: This fails every build after this because of redefinitions... @@ -168,6 +172,14 @@ public class DalamudBuild : NukeBuild .EnableNoRestore()); }); + Target CompileInjectorBoot => _ => _ + .Executes(() => + { + MSBuildTasks.MSBuild(s => s + .SetTargetPath(InjectorBootProjectFile) + .SetConfiguration(Configuration)); + }); + Target SetCILogging => _ => _ .DependentFor(Compile) .OnlyWhenStatic(() => IsCIBuild) @@ -184,6 +196,7 @@ public class DalamudBuild : NukeBuild .DependsOn(CompileDalamudBoot) .DependsOn(CompileDalamudCrashHandler) .DependsOn(CompileInjector) + .DependsOn(CompileInjectorBoot) ; Target CI => _ => _ @@ -237,6 +250,12 @@ public class DalamudBuild : NukeBuild .SetProject(InjectorProjectFile) .SetConfiguration(Configuration)); - ArtifactsDirectory.CreateOrCleanDirectory(); + MSBuildTasks.MSBuild(s => s + .SetProjectFile(InjectorBootProjectFile) + .SetConfiguration(Configuration) + .SetTargets("Clean")); + + FileSystemTasks.DeleteDirectory(ArtifactsDirectory); + Directory.CreateDirectory(ArtifactsDirectory); }); } diff --git a/build/build.csproj b/build/build.csproj index 7096c7f8a..b4aaa959d 100644 --- a/build/build.csproj +++ b/build/build.csproj @@ -11,8 +11,7 @@ <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally> </PropertyGroup> <ItemGroup> - <PackageReference Include="Nuke.Common" Version="10.1.0" /> - <PackageReference Include="System.Runtime.Serialization.Formatters" Version="10.0.0" /> - <PackageReference Remove="Microsoft.CodeAnalysis.BannedApiAnalyzers" /> + <PackageReference Include="Nuke.Common" Version="6.2.1" /> + <PackageReference Include="System.Runtime.Serialization.Formatters" Version="9.0.0" /> </ItemGroup> </Project> diff --git a/docfx.json b/docfx.json index cf7a80194..30c85957c 100644 --- a/docfx.json +++ b/docfx.json @@ -4,17 +4,18 @@ "src": [ { "files": [ - "bin/Release/Dalamud.dll" + "Dalamud.Interface/Dalamud.Interface.csproj", + "Dalamud/Dalamud.csproj", + "lib/ImGuiScene/ImGuiScene/ImGuiScene.csproj", + "lib/ImGuiScene/deps/ImGui.NET/src/ImGui.NET-472/ImGui.NET-472.csproj", + "lib/ImGuiScene/deps/SDL2-CS/SDL2-CS.csproj" ] } ], "dest": "api", "disableGitFeatures": false, "disableDefaultFilter": false, - "filter": "filterConfig.yml", - "properties": { - "TargetFramework": "net10.0-windows" - } + "filter": "filterConfig.yml" } ], "build": { diff --git a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Sample/Dalamud.EnumGenerator.Sample.csproj b/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Sample/Dalamud.EnumGenerator.Sample.csproj deleted file mode 100644 index 225ea5f94..000000000 --- a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Sample/Dalamud.EnumGenerator.Sample.csproj +++ /dev/null @@ -1,20 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <PropertyGroup> - <TargetFramework>net9.0</TargetFramework> - <Nullable>enable</Nullable> - <RootNamespace>Dalamud.EnumGenerator.Sample</RootNamespace> - - <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally> - </PropertyGroup> - - <ItemGroup> - <ProjectReference Include="..\Dalamud.EnumGenerator\Dalamud.EnumGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/> - </ItemGroup> - - <ItemGroup> - <None Remove="EnumCloneMap.txt"/> - <AdditionalFiles Include="EnumCloneMap.txt" /> - </ItemGroup> - -</Project> diff --git a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Sample/EnumCloneMap.txt b/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Sample/EnumCloneMap.txt deleted file mode 100644 index a7db08bf3..000000000 --- a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Sample/EnumCloneMap.txt +++ /dev/null @@ -1,4 +0,0 @@ -# Format: Target.Full.TypeName = Source.Full.EnumTypeName -# Example: Generate a local enum MyGeneratedEnum in namespace Sample.Gen mapped to SourceEnums.SampleSourceEnum -Dalamud.EnumGenerator.Sample.Gen.MyGeneratedEnum = Dalamud.EnumGenerator.Sample.SourceEnums.SampleSourceEnum - diff --git a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Sample/SourceEnums.cs b/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Sample/SourceEnums.cs deleted file mode 100644 index 407b4c151..000000000 --- a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Sample/SourceEnums.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Dalamud.EnumGenerator.Sample.SourceEnums -{ - public enum SampleSourceEnum : long - { - First = 1, - Second = 2, - Third = 10000000000L - } -} diff --git a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Tests/Dalamud.EnumGenerator.Tests.csproj b/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Tests/Dalamud.EnumGenerator.Tests.csproj deleted file mode 100644 index 50de4a7c8..000000000 --- a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Tests/Dalamud.EnumGenerator.Tests.csproj +++ /dev/null @@ -1,29 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <PropertyGroup> - <TargetFramework>net9.0</TargetFramework> - <Nullable>enable</Nullable> - - <IsPackable>false</IsPackable> - - <RootNamespace>Dalamud.EnumGenerator.Tests</RootNamespace> - - <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally> - </PropertyGroup> - - <ItemGroup> - <PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.1"/> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2"/> - <PackageReference Include="xunit" Version="2.4.2"/> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - <PrivateAssets>all</PrivateAssets> - </PackageReference> - </ItemGroup> - - <ItemGroup> - <ProjectReference Include="..\Dalamud.EnumGenerator\Dalamud.EnumGenerator.csproj"/> - </ItemGroup> - - -</Project> diff --git a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Tests/EnumCloneMapTests.cs b/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Tests/EnumCloneMapTests.cs deleted file mode 100644 index f14279c53..000000000 --- a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Tests/EnumCloneMapTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Immutable; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Xunit; - -namespace Dalamud.EnumGenerator.Tests; - -public class EnumCloneMapTests -{ - [Fact] - public void ParseMappings_SimpleLines_ParsesCorrectly() - { - var text = @"# Comment line -My.Namespace.Target = Other.Namespace.Source - -Another.Target = Some.Source"; - - var results = Dalamud.EnumGenerator.EnumCloneGenerator.ParseMappings(text); - - Assert.Equal(2, results.Length); - Assert.Equal("My.Namespace.Target", results[0].TargetFullName); - Assert.Equal("Other.Namespace.Source", results[0].SourceFullName); - Assert.Equal("Another.Target", results[1].TargetFullName); - } - - [Fact] - public void Generator_ProducesFile_WhenSourceResolved() - { - // We'll create a compilation that contains a source enum type and add an AdditionalText mapping - var sourceEnum = @"namespace Foo.Bar { public enum SourceEnum { A = 1, B = 2 } }"; - - var mapText = "GeneratedNs.TargetEnum = Foo.Bar.SourceEnum"; - - var generator = new EnumCloneGenerator(); - var driver = CSharpGeneratorDriver.Create(generator) - .AddAdditionalTexts(ImmutableArray.Create<AdditionalText>(new Utils.TestAdditionalFile("EnumCloneMap.txt", mapText))); - - var compilation = CSharpCompilation.Create("TestGen", [CSharpSyntaxTree.ParseText(sourceEnum)], - [MetadataReference.CreateFromFile(typeof(object).Assembly.Location)]); - - driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out var diagnostics); - - var generated = newCompilation.SyntaxTrees.Select(t => t.FilePath).Where(p => p.EndsWith("TargetEnum.CloneEnum.g.cs")).ToArray(); - Assert.Single(generated); - } -} diff --git a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Tests/Utils/TestAdditionalFile.cs b/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Tests/Utils/TestAdditionalFile.cs deleted file mode 100644 index e5c0df848..000000000 --- a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator.Tests/Utils/TestAdditionalFile.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Threading; - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Text; - -namespace Dalamud.EnumGenerator.Tests.Utils; - -public class TestAdditionalFile : AdditionalText -{ - private readonly SourceText text; - - public TestAdditionalFile(string path, string text) - { - Path = path; - this.text = SourceText.From(text); - } - - public override SourceText GetText(CancellationToken cancellationToken = new()) => this.text; - - public override string Path { get; } -} diff --git a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/AnalyzerReleases.Shipped.md b/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/AnalyzerReleases.Shipped.md deleted file mode 100644 index 60b59dd99..000000000 --- a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/AnalyzerReleases.Shipped.md +++ /dev/null @@ -1,3 +0,0 @@ -; Shipped analyzer releases -; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md - diff --git a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/AnalyzerReleases.Unshipped.md b/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/AnalyzerReleases.Unshipped.md deleted file mode 100644 index e90084796..000000000 --- a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/AnalyzerReleases.Unshipped.md +++ /dev/null @@ -1,9 +0,0 @@ -; Unshipped analyzer release -; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md - -### New Rules - -Rule ID | Category | Severity | Notes ---------|----------|----------|------- -ENUMGEN001 | EnumGenerator | Warning | SourceGeneratorWithAttributes -ENUMGEN002 | EnumGenerator | Warning | SourceGeneratorWithAttributes \ No newline at end of file diff --git a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/Dalamud.EnumGenerator.csproj b/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/Dalamud.EnumGenerator.csproj deleted file mode 100644 index 106b036a8..000000000 --- a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/Dalamud.EnumGenerator.csproj +++ /dev/null @@ -1,33 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <PropertyGroup> - <TargetFramework>netstandard2.0</TargetFramework> - <IsPackable>false</IsPackable> - <Nullable>enable</Nullable> - <LangVersion>latest</LangVersion> - - <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> - <IsRoslynComponent>true</IsRoslynComponent> - - <RootNamespace>Dalamud.EnumGenerator</RootNamespace> - <PackageId>Dalamud.EnumGenerator</PackageId> - - <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally> - </PropertyGroup> - - <ItemGroup> - <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4"> - <PrivateAssets>all</PrivateAssets> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - </PackageReference> - <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0"/> - <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.3.0"/> - </ItemGroup> - - <ItemGroup> - <AdditionalFiles Include="AnalyzerReleases.Shipped.md" /> - <AdditionalFiles Include="AnalyzerReleases.Unshipped.md" /> - </ItemGroup> - - -</Project> diff --git a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/EnumCloneGenerator.cs b/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/EnumCloneGenerator.cs deleted file mode 100644 index 10cf0723c..000000000 --- a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/EnumCloneGenerator.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System; -using System.Collections.Immutable; -using System.IO; -using System.Linq; -using System.Text; -using System.Globalization; - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Text; - -namespace Dalamud.EnumGenerator; - -[Generator] -public class EnumCloneGenerator : IIncrementalGenerator -{ - private const string NewLine = "\r\n"; - - private const string MappingFileName = "EnumCloneMap.txt"; - - private static readonly DiagnosticDescriptor MissingSourceDescriptor = new( - id: "ENUMGEN001", - title: "Source enum not found", - messageFormat: "Source enum '{0}' could not be resolved by the compilation", - category: "EnumGenerator", - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); - - private static readonly DiagnosticDescriptor DuplicateTargetDescriptor = new( - id: "ENUMGEN002", - title: "Duplicate target mapping", - messageFormat: "Target enum '{0}' is mapped multiple times; generation skipped for this target", - category: "EnumGenerator", - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); - - public void Initialize(IncrementalGeneratorInitializationContext context) - { - // Read mappings from additional files named EnumCloneMap.txt - var mappingEntries = context.AdditionalTextsProvider - .Where(at => Path.GetFileName(at.Path).Equals(MappingFileName, StringComparison.OrdinalIgnoreCase)) - .SelectMany((at, _) => ParseMappings(at.GetText()?.ToString() ?? string.Empty)); - - // Combine with compilation so we can resolve types - var compilationAndMaps = context.CompilationProvider.Combine(mappingEntries.Collect()); - - context.RegisterSourceOutput(compilationAndMaps, (spc, pair) => - { - var compilation = pair.Left; - var maps = pair.Right; - - // Detect duplicate targets first and report diagnostics - var duplicateTargets = maps.GroupBy(m => m.TargetFullName, StringComparer.OrdinalIgnoreCase) - .Where(g => g.Count() > 1) - .Select(g => g.Key) - .ToImmutableArray(); - foreach (var dup in duplicateTargets) - { - var diag = Diagnostic.Create(DuplicateTargetDescriptor, Location.None, dup); - spc.ReportDiagnostic(diag); - } - - foreach (var (targetFullName, sourceFullName) in maps) - { - if (string.IsNullOrWhiteSpace(targetFullName) || string.IsNullOrWhiteSpace(sourceFullName)) - continue; - - if (duplicateTargets.Contains(targetFullName, StringComparer.OrdinalIgnoreCase)) - continue; - - // Resolve the source enum type by metadata name (namespace.type) - var sourceSymbol = compilation.GetTypeByMetadataName(sourceFullName); - if (sourceSymbol is null) - { - // Report diagnostic for missing source type - var diag = Diagnostic.Create(MissingSourceDescriptor, Location.None, sourceFullName); - spc.ReportDiagnostic(diag); - continue; - } - - if (sourceSymbol.TypeKind != TypeKind.Enum) - continue; - - var sourceNamed = sourceSymbol; // GetTypeByMetadataName already returns INamedTypeSymbol - - // Split target into namespace and type name - string? targetNamespace = null; - var targetName = targetFullName; - var lastDot = targetFullName.LastIndexOf('.'); - if (lastDot >= 0) - { - targetNamespace = targetFullName.Substring(0, lastDot); - targetName = targetFullName.Substring(lastDot + 1); - } - - var underlyingType = sourceNamed.EnumUnderlyingType; - var underlyingDisplay = underlyingType?.ToDisplayString() ?? "int"; - - var fields = sourceNamed.GetMembers() - .OfType<IFieldSymbol>() - .Where(f => f.IsStatic && f.HasConstantValue) - .ToArray(); - - var memberLines = fields.Select(f => - { - var name = f.Name; - var constValue = f.ConstantValue; - string literal; - - var st = underlyingType?.SpecialType ?? SpecialType.System_Int32; - - if (constValue is null) - { - literal = "0"; - } - else if (st == SpecialType.System_UInt64) - { - literal = Convert.ToString(constValue, CultureInfo.InvariantCulture) + "UL"; - } - else if (st == SpecialType.System_UInt32) - { - literal = Convert.ToString(constValue, CultureInfo.InvariantCulture) + "U"; - } - else if (st == SpecialType.System_Int64) - { - literal = Convert.ToString(constValue, CultureInfo.InvariantCulture) + "L"; - } - else - { - literal = Convert.ToString(constValue, CultureInfo.InvariantCulture) ?? throw new InvalidOperationException("Unable to convert enum constant value to string."); - } - - return $" {name} = {literal},"; - }); - - var membersText = string.Join(NewLine, memberLines); - - var nsPrefix = targetNamespace is null ? string.Empty : $"namespace {targetNamespace};" + NewLine + NewLine; - - var sourceFullyQualified = sourceNamed.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - - var code = "// <auto-generated/>" + NewLine + NewLine - + nsPrefix - + $"public enum {targetName} : {underlyingDisplay}" + NewLine - + "{" + NewLine - + membersText + NewLine - + "}" + NewLine + NewLine; - - var extClassName = targetName + "Conversions"; - var extMethodName = "ToDalamud" + targetName; - - var extClass = $"public static class {extClassName}" + NewLine - + "{" + NewLine - + $" public static {targetName} {extMethodName}(this {sourceFullyQualified} value) => ({targetName})(({underlyingDisplay})value);" + NewLine - + "}" + NewLine; - - code += extClass; - - var hintName = $"{targetName}.CloneEnum.g.cs"; - spc.AddSource(hintName, SourceText.From(code, Encoding.UTF8)); - } - }); - } - - internal static ImmutableArray<(string TargetFullName, string SourceFullName)> ParseMappings(string text) - { - var builder = ImmutableArray.CreateBuilder<(string, string)>(); - using var reader = new StringReader(text); - string? line; - while ((line = reader.ReadLine()) != null) - { - // Remove comments starting with # - var commentIndex = line.IndexOf('#'); - var content = commentIndex >= 0 ? line.Substring(0, commentIndex) : line; - content = content.Trim(); - if (string.IsNullOrEmpty(content)) - continue; - - // Expected format: Target.Full.Name = Source.Full.Name - var idx = content.IndexOf('='); - if (idx <= 0) - continue; - - var left = content.Substring(0, idx).Trim(); - var right = content.Substring(idx + 1).Trim(); - if (string.IsNullOrEmpty(left) || string.IsNullOrEmpty(right)) - continue; - - builder.Add((left, right)); - } - - return builder.ToImmutable(); - } -} diff --git a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/Properties/AssemblyInfo.cs b/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/Properties/AssemblyInfo.cs deleted file mode 100644 index 6eac4d12e..000000000 --- a/generators/Dalamud.EnumGenerator/Dalamud.EnumGenerator/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Dalamud.EnumGenerator.Tests")] - diff --git a/generators/Directory.Build.props b/generators/Directory.Build.props deleted file mode 100644 index f699838f7..000000000 --- a/generators/Directory.Build.props +++ /dev/null @@ -1,5 +0,0 @@ -<Project> - <ItemGroup Label="Code Analysis"> - <PackageReference Remove="Microsoft.CodeAnalysis.BannedApiAnalyzers" /> - </ItemGroup> -</Project> diff --git a/global.json b/global.json index 93dd0dd1f..ab1a4a2ec 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { "sdk": { - "version": "10.0.0", + "version": "9.0.0", "rollForward": "latestMinor", "allowPrerelease": true } -} \ No newline at end of file +} diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.DragScalar.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.DragScalar.cs index 3cf20bb30..665fa434f 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.DragScalar.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.DragScalar.cs @@ -238,7 +238,7 @@ public static unsafe partial class ImGui ImGuiSliderFlags flags = ImGuiSliderFlags.None) => DragScalar( label, ImGuiDataType.Float, - MemoryMarshal.Cast<Vector2, float>(new Span<Vector2>(ref v)), + MemoryMarshal.Cast<Vector2, float>(new(ref v)), vSpeed, vMin, vMax, @@ -251,7 +251,7 @@ public static unsafe partial class ImGui ImGuiSliderFlags flags = ImGuiSliderFlags.None) => DragScalar( label, ImGuiDataType.Float, - MemoryMarshal.Cast<Vector3, float>(new Span<Vector3>(ref v)), + MemoryMarshal.Cast<Vector3, float>(new(ref v)), vSpeed, vMin, vMax, @@ -264,7 +264,7 @@ public static unsafe partial class ImGui ImGuiSliderFlags flags = ImGuiSliderFlags.None) => DragScalar( label, ImGuiDataType.Float, - MemoryMarshal.Cast<Vector4, float>(new Span<Vector4>(ref v)), + MemoryMarshal.Cast<Vector4, float>(new(ref v)), vSpeed, vMin, vMax, diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.InputScalar.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.InputScalar.cs index 5881ac462..fb86096ff 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.InputScalar.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.InputScalar.cs @@ -205,7 +205,7 @@ public static unsafe partial class ImGui InputScalar( label, ImGuiDataType.Float, - MemoryMarshal.Cast<Vector2, float>(new Span<Vector2>(ref data)), + MemoryMarshal.Cast<Vector2, float>(new(ref data)), step, stepFast, format.MoveOrDefault("%.3f"u8), @@ -219,7 +219,7 @@ public static unsafe partial class ImGui InputScalar( label, ImGuiDataType.Float, - MemoryMarshal.Cast<Vector3, float>(new Span<Vector3>(ref data)), + MemoryMarshal.Cast<Vector3, float>(new(ref data)), step, stepFast, format.MoveOrDefault("%.3f"u8), @@ -233,7 +233,7 @@ public static unsafe partial class ImGui InputScalar( label, ImGuiDataType.Float, - MemoryMarshal.Cast<Vector4, float>(new Span<Vector4>(ref data)), + MemoryMarshal.Cast<Vector4, float>(new(ref data)), step, stepFast, format.MoveOrDefault("%.3f"u8), diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Manual.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Manual.cs index e455e0778..89b3cc3d6 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Manual.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Manual.cs @@ -127,14 +127,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputText(label, t.Buffer[..(maxLength + 1)], flags, callback); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -146,14 +140,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputText(label, t.Buffer[..(maxLength + 1)], flags, callback); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -165,14 +153,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputText(label, t.Buffer[..(maxLength + 1)], flags, callback, ref context); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -184,14 +166,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputText(label, t.Buffer[..(maxLength + 1)], flags, callback, in context); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -311,14 +287,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputTextEx(label, hint, t.Buffer[..(maxLength + 1)], sizeArg, flags, callback); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -330,14 +300,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputTextEx(label, hint, t.Buffer[..(maxLength + 1)], sizeArg, flags, callback); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -350,14 +314,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputTextEx(label, hint, t.Buffer[..(maxLength + 1)], sizeArg, flags, callback, ref context); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -370,14 +328,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputTextEx(label, hint, t.Buffer[..(maxLength + 1)], sizeArg, flags, callback, in context); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -436,14 +388,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputTextMultiline(label, t.Buffer[..(maxLength + 1)], size, flags, callback); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -455,14 +401,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputTextMultiline(label, t.Buffer[..(maxLength + 1)], size, flags, callback); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -474,14 +414,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputTextMultiline(label, t.Buffer[..(maxLength + 1)], size, flags, callback, ref context); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -493,14 +427,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputTextMultiline(label, t.Buffer[..(maxLength + 1)], size, flags, callback, in context); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -549,14 +477,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputTextWithHint(label, hint, t.Buffer[..(maxLength + 1)], flags, callback); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -568,14 +490,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputTextWithHint(label, hint, t.Buffer[..(maxLength + 1)], flags, callback); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -587,14 +503,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputTextWithHint(label, hint, t.Buffer[..(maxLength + 1)], flags, callback, ref context); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -606,14 +516,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputTextWithHint(label, hint, t.Buffer[..(maxLength + 1)], flags, callback, in context); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } @@ -637,14 +541,8 @@ public unsafe partial class ImGui var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = TempInputText(bb, id, label, t.Buffer[..(maxLength + 1)], flags); - - var e = (flags & ImGuiInputTextFlags.EnterReturnsTrue) != 0; - if (r | e) - { - var i = t.Buffer.IndexOf((byte)0); - buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - } - + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); t.Recycle(); return r; } diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.SliderScalar.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.SliderScalar.cs index b0c4b7c79..20ee78ab6 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.SliderScalar.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.SliderScalar.cs @@ -210,7 +210,7 @@ public static unsafe partial class ImGui ImU8String format = default, ImGuiSliderFlags flags = ImGuiSliderFlags.None) => SliderScalar( label, ImGuiDataType.Float, - MemoryMarshal.Cast<Vector2, float>(new Span<Vector2>(ref v)), + MemoryMarshal.Cast<Vector2, float>(new(ref v)), vMin, vMax, format.MoveOrDefault("%.3f"u8), @@ -222,7 +222,7 @@ public static unsafe partial class ImGui SliderScalar( label, ImGuiDataType.Float, - MemoryMarshal.Cast<Vector3, float>(new Span<Vector3>(ref v)), + MemoryMarshal.Cast<Vector3, float>(new(ref v)), vMin, vMax, format.MoveOrDefault("%.3f"u8), @@ -236,7 +236,7 @@ public static unsafe partial class ImGui SliderScalar( label, ImGuiDataType.Float, - MemoryMarshal.Cast<Vector4, float>(new Span<Vector4>(ref v)), + MemoryMarshal.Cast<Vector4, float>(new(ref v)), vMin, vMax, format.MoveOrDefault("%.3f"u8), diff --git a/imgui/Dalamud.Bindings.ImGui/ImU8String.cs b/imgui/Dalamud.Bindings.ImGui/ImU8String.cs index f2b635764..a62152c39 100644 --- a/imgui/Dalamud.Bindings.ImGui/ImU8String.cs +++ b/imgui/Dalamud.Bindings.ImGui/ImU8String.cs @@ -156,7 +156,7 @@ public ref struct ImU8String return this.rentedBuffer is { } buf ? buf.AsSpan() - : MemoryMarshal.Cast<FixedBufferContainer, byte>(new Span<FixedBufferContainer>(ref Unsafe.AsRef(ref this.fixedBuffer))); + : MemoryMarshal.Cast<FixedBufferContainer, byte>(new(ref Unsafe.AsRef(ref this.fixedBuffer))); } } @@ -165,7 +165,7 @@ public ref struct ImU8String private ref byte FixedBufferByteRef => ref this.FixedBufferSpan[0]; private Span<byte> FixedBufferSpan => - MemoryMarshal.Cast<FixedBufferContainer, byte>(new Span<FixedBufferContainer>(ref Unsafe.AsRef(ref this.fixedBuffer))); + MemoryMarshal.Cast<FixedBufferContainer, byte>(new(ref Unsafe.AsRef(ref this.fixedBuffer))); public static implicit operator ImU8String(ReadOnlySpan<byte> text) => new(text); public static implicit operator ImU8String(ReadOnlyMemory<byte> text) => new(text); diff --git a/imgui/Dalamud.Bindings.ImGui/ImVector.cs b/imgui/Dalamud.Bindings.ImGui/ImVector.cs index 67e450193..9a10c1d6b 100644 --- a/imgui/Dalamud.Bindings.ImGui/ImVector.cs +++ b/imgui/Dalamud.Bindings.ImGui/ImVector.cs @@ -1,12 +1,7 @@ -using System.Collections; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; namespace Dalamud.Bindings.ImGui; -/// <summary> -/// A structure representing a dynamic array for unmanaged types. -/// </summary> public unsafe struct ImVector { public readonly int Size; @@ -20,23 +15,23 @@ public unsafe struct ImVector Data = data; } - public readonly ref T Ref<T>(int index) => ref Unsafe.AsRef<T>((byte*)this.Data + (index * Unsafe.SizeOf<T>())); + public ref T Ref<T>(int index) + { + return ref Unsafe.AsRef<T>((byte*)Data + index * Unsafe.SizeOf<T>()); + } - public readonly nint Address<T>(int index) => (nint)((byte*)this.Data + (index * Unsafe.SizeOf<T>())); + public IntPtr Address<T>(int index) + { + return (IntPtr)((byte*)Data + index * Unsafe.SizeOf<T>()); + } } /// <summary> /// A structure representing a dynamic array for unmanaged types. /// </summary> /// <typeparam name="T">The type of elements in the vector, must be unmanaged.</typeparam> -[StructLayout(LayoutKind.Sequential)] -public unsafe struct ImVector<T> : IEnumerable<T> - where T : unmanaged +public unsafe struct ImVector<T> where T : unmanaged { - private int size; - private int capacity; - private T* data; - /// <summary> /// Initializes a new instance of the <see cref="ImVector{T}"/> struct with the specified size, capacity, and data pointer. /// </summary> @@ -50,6 +45,11 @@ public unsafe struct ImVector<T> : IEnumerable<T> this.data = data; } + private int size; + private int capacity; + private unsafe T* data; + + /// <summary> /// Gets or sets the element at the specified index. /// </summary> @@ -58,72 +58,80 @@ public unsafe struct ImVector<T> : IEnumerable<T> /// <exception cref="IndexOutOfRangeException">Thrown when the index is out of range.</exception> public T this[int index] { - readonly get + get { - if (index < 0 || index >= this.size) + if (index < 0 || index >= size) + { throw new IndexOutOfRangeException(); - return this.data[index]; + } + return data[index]; } set { - if (index < 0 || index >= this.size) + if (index < 0 || index >= size) + { throw new IndexOutOfRangeException(); - this.data[index] = value; + } + data[index] = value; } } /// <summary> /// Gets a pointer to the first element of the vector. /// </summary> - public readonly T* Data => this.data; + public readonly T* Data => data; /// <summary> /// Gets a pointer to the first element of the vector. /// </summary> - public readonly T* Front => this.data; + public readonly T* Front => data; /// <summary> /// Gets a pointer to the last element of the vector. /// </summary> - public readonly T* Back => this.size > 0 ? this.data + this.size - 1 : null; + public readonly T* Back => size > 0 ? data + size - 1 : null; /// <summary> /// Gets or sets the capacity of the vector. /// </summary> public int Capacity { - readonly get => this.capacity; + readonly get => capacity; set { - ArgumentOutOfRangeException.ThrowIfLessThan(value, this.size, nameof(Capacity)); - if (this.capacity == value) - return; - - if (this.data == null) + if (capacity == value) { - this.data = (T*)ImGui.MemAlloc((nuint)(value * sizeof(T))); + return; + } + + if (data == null) + { + data = (T*)ImGui.MemAlloc((nuint)(value * sizeof(T))); } else { - var newSize = Math.Min(this.size, value); - var newData = (T*)ImGui.MemAlloc((nuint)(value * sizeof(T))); - Buffer.MemoryCopy(this.data, newData, (nuint)(value * sizeof(T)), (nuint)(newSize * sizeof(T))); - ImGui.MemFree(this.data); - this.data = newData; - this.size = newSize; + int newSize = Math.Min(size, value); + T* newData = (T*)ImGui.MemAlloc((nuint)(value * sizeof(T))); + Buffer.MemoryCopy(data, newData, (nuint)(value * sizeof(T)), (nuint)(newSize * sizeof(T))); + ImGui.MemFree(data); + data = newData; + size = newSize; } - this.capacity = value; + capacity = value; // Clear the rest of the data - new Span<T>(this.data + this.size, this.capacity - this.size).Clear(); + for (int i = size; i < capacity; i++) + { + data[i] = default; + } } } /// <summary> /// Gets the number of elements in the vector. /// </summary> - public readonly int Size => this.size; + public readonly int Size => size; /// <summary> /// Grows the capacity of the vector to at least the specified value. @@ -131,8 +139,10 @@ public unsafe struct ImVector<T> : IEnumerable<T> /// <param name="newCapacity">The new capacity.</param> public void Grow(int newCapacity) { - var newCapacity2 = this.capacity > 0 ? this.capacity + (this.capacity / 2) : 8; - this.Capacity = newCapacity2 > newCapacity ? newCapacity2 : newCapacity; + if (newCapacity > capacity) + { + Capacity = newCapacity * 2; + } } /// <summary> @@ -141,8 +151,10 @@ public unsafe struct ImVector<T> : IEnumerable<T> /// <param name="size">The minimum capacity required.</param> public void EnsureCapacity(int size) { - if (size > this.capacity) + if (size > capacity) + { Grow(size); + } } /// <summary> @@ -152,46 +164,25 @@ public unsafe struct ImVector<T> : IEnumerable<T> public void Resize(int newSize) { EnsureCapacity(newSize); - this.size = newSize; + size = newSize; } /// <summary> /// Clears all elements from the vector. /// </summary> - public void Clear() => this.size = 0; + public void Clear() + { + size = 0; + } /// <summary> /// Adds an element to the end of the vector. /// </summary> /// <param name="value">The value to add.</param> - [OverloadResolutionPriority(1)] public void PushBack(T value) { - this.EnsureCapacity(this.size + 1); - this.data[this.size++] = value; - } - - /// <summary> - /// Adds an element to the end of the vector. - /// </summary> - /// <param name="value">The value to add.</param> - [OverloadResolutionPriority(2)] - public void PushBack(in T value) - { - EnsureCapacity(this.size + 1); - this.data[this.size++] = value; - } - - /// <summary> - /// Adds an element to the front of the vector. - /// </summary> - /// <param name="value">The value to add.</param> - public void PushFront(in T value) - { - if (this.size == 0) - this.PushBack(value); - else - this.Insert(0, value); + EnsureCapacity(size + 1); + data[size++] = value; } /// <summary> @@ -199,126 +190,48 @@ public unsafe struct ImVector<T> : IEnumerable<T> /// </summary> public void PopBack() { - if (this.size > 0) + if (size > 0) { - this.size--; + size--; } } - public ref T Insert(int index, in T v) { - ArgumentOutOfRangeException.ThrowIfNegative(index, nameof(index)); - ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, this.size, nameof(index)); - this.EnsureCapacity(this.size + 1); - if (index < this.size) - { - Buffer.MemoryCopy( - this.data + index, - this.data + index + 1, - (this.size - index) * sizeof(T), - (this.size - index) * sizeof(T)); - } - - this.data[index] = v; - this.size++; - return ref this.data[index]; - } - - public Span<T> InsertRange(int index, ReadOnlySpan<T> v) - { - ArgumentOutOfRangeException.ThrowIfNegative(index, nameof(index)); - ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, this.size, nameof(index)); - this.EnsureCapacity(this.size + v.Length); - if (index < this.size) - { - Buffer.MemoryCopy( - this.data + index, - this.data + index + v.Length, - (this.size - index) * sizeof(T), - (this.size - index) * sizeof(T)); - } - - var dstSpan = new Span<T>(this.data + index, v.Length); - v.CopyTo(new(this.data + index, v.Length)); - this.size += v.Length; - return dstSpan; - } - /// <summary> /// Frees the memory allocated for the vector. /// </summary> public void Free() { - if (this.data != null) + if (data != null) { - ImGui.MemFree(this.data); - this.data = null; - this.size = 0; - this.capacity = 0; + ImGui.MemFree(data); + data = null; + size = 0; + capacity = 0; } } - public readonly ref T Ref(int index) + public ref T Ref(int index) { - return ref Unsafe.AsRef<T>((byte*)Data + (index * Unsafe.SizeOf<T>())); + return ref Unsafe.AsRef<T>((byte*)Data + index * Unsafe.SizeOf<T>()); } - public readonly ref TCast Ref<TCast>(int index) + public ref TCast Ref<TCast>(int index) { - return ref Unsafe.AsRef<TCast>((byte*)Data + (index * Unsafe.SizeOf<TCast>())); + return ref Unsafe.AsRef<TCast>((byte*)Data + index * Unsafe.SizeOf<TCast>()); } - public readonly void* Address(int index) + public void* Address(int index) { - return (byte*)Data + (index * Unsafe.SizeOf<T>()); + return (byte*)Data + index * Unsafe.SizeOf<T>(); } - public readonly void* Address<TCast>(int index) + public void* Address<TCast>(int index) { - return (byte*)Data + (index * Unsafe.SizeOf<TCast>()); + return (byte*)Data + index * Unsafe.SizeOf<TCast>(); } - public readonly ImVector* ToUntyped() + public ImVector* ToUntyped() { - return (ImVector*)Unsafe.AsPointer(ref Unsafe.AsRef(in this)); - } - - public readonly Span<T> AsSpan() => new(this.data, this.size); - - public readonly Enumerator GetEnumerator() => new(this.data, this.data + this.size); - - readonly IEnumerator<T> IEnumerable<T>.GetEnumerator() => this.GetEnumerator(); - - readonly IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); - - public struct Enumerator(T* begin, T* end) : IEnumerator<T>, IEnumerable<T> - { - private T* current = null; - - public readonly ref T Current => ref *this.current; - - readonly T IEnumerator<T>.Current => this.Current; - - readonly object IEnumerator.Current => this.Current; - - public bool MoveNext() - { - var next = this.current == null ? begin : this.current + 1; - if (next == end) - return false; - this.current = next; - return true; - } - - public void Reset() => this.current = null; - - public readonly Enumerator GetEnumerator() => new(begin, end); - - readonly void IDisposable.Dispose() - { - } - - readonly IEnumerator<T> IEnumerable<T>.GetEnumerator() => this.GetEnumerator(); - - readonly IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + return (ImVector*)Unsafe.AsPointer(ref this); } } diff --git a/imgui/StandaloneImGuiTestbed/StandaloneImGuiTestbed.csproj b/imgui/StandaloneImGuiTestbed/StandaloneImGuiTestbed.csproj index da31c9a8e..d56faa31e 100644 --- a/imgui/StandaloneImGuiTestbed/StandaloneImGuiTestbed.csproj +++ b/imgui/StandaloneImGuiTestbed/StandaloneImGuiTestbed.csproj @@ -26,7 +26,6 @@ <PackageReference Include="Veldrid" Version="4.9.0" /> <PackageReference Include="Veldrid.SDL2" Version="4.9.0" /> <PackageReference Include="Veldrid.StartupUtilities" Version="4.9.0" /> - <PackageReference Remove="Microsoft.CodeAnalysis.BannedApiAnalyzers"/> </ItemGroup> <ItemGroup> diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index a97e9f89d..0769d1f18 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit a97e9f89d72d40eabd0f3b52266862dca3eba872 +Subproject commit 0769d1f180f859688f47a7a99610e9ce10da946c diff --git a/lib/Lumina.Excel b/lib/Lumina.Excel index 31e50c3f2..5d01489c3 160000 --- a/lib/Lumina.Excel +++ b/lib/Lumina.Excel @@ -1 +1 @@ -Subproject commit 31e50c3f267dd845891b328140106a0cc3b1f35e +Subproject commit 5d01489c34f33a3d645f49085d7fc0065a1ac801 diff --git a/lib/cimgui b/lib/cimgui index bc3272967..27c8565f6 160000 --- a/lib/cimgui +++ b/lib/cimgui @@ -1 +1 @@ -Subproject commit bc327296758d57d3bdc963cb6ce71dd5b0c7e54c +Subproject commit 27c8565f631b004c3266373890e41ecc627f775b diff --git a/targets/Dalamud.Plugin.Bootstrap.targets b/targets/Dalamud.Plugin.Bootstrap.targets new file mode 100644 index 000000000..db4bf6cd7 --- /dev/null +++ b/targets/Dalamud.Plugin.Bootstrap.targets @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project> + <PropertyGroup> + <DalamudLibPath Condition="$([MSBuild]::IsOSPlatform('Windows'))">$(appdata)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath> + <DalamudLibPath Condition="$([MSBuild]::IsOSPlatform('Linux'))">$(HOME)/.xlcore/dalamud/Hooks/dev/</DalamudLibPath> + <DalamudLibPath Condition="$([MSBuild]::IsOSPlatform('OSX'))">$(HOME)/Library/Application Support/XIV on Mac/dalamud/Hooks/dev/</DalamudLibPath> + <DalamudLibPath Condition="$(DALAMUD_HOME) != ''">$(DALAMUD_HOME)/</DalamudLibPath> + </PropertyGroup> + + <Import Project="$(DalamudLibPath)/targets/Dalamud.Plugin.targets"/> +</Project> diff --git a/targets/Dalamud.Plugin.targets b/targets/Dalamud.Plugin.targets new file mode 100644 index 000000000..08d19735e --- /dev/null +++ b/targets/Dalamud.Plugin.targets @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project> + <PropertyGroup> + <TargetFramework>net8.0-windows</TargetFramework> + <Platforms>x64</Platforms> + <Nullable>enable</Nullable> + <LangVersion>latest</LangVersion> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + <ProduceReferenceAssembly>false</ProduceReferenceAssembly> + <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> + <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile> + <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> + <AssemblySearchPaths>$(AssemblySearchPaths);$(DalamudLibPath)</AssemblySearchPaths> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="DalamudPackager" Version="11.0.0" /> + <Reference Include="FFXIVClientStructs" Private="false" /> + <Reference Include="Newtonsoft.Json" Private="false" /> + <Reference Include="Dalamud" Private="false" /> + <Reference Include="ImGui.NET" Private="false" /> + <Reference Include="ImGuiScene" Private="false" /> + <Reference Include="Lumina" Private="false" /> + <Reference Include="Lumina.Excel" Private="false" /> + <Reference Include="Serilog" Private="false" /> + </ItemGroup> + + <Target Name="Message" BeforeTargets="BeforeBuild"> + <Message Text="Dalamud.Plugin: root at $(DalamudLibPath)" Importance="high" /> + </Target> + + <Target Name="DeprecationNotice" BeforeTargets="BeforeBuild"> + <Warning Text="Using the targets file to include the Dalamud SDK is no longer recommended. Please upgrade to Dalamud.NET.Sdk - learn more here: https://dalamud.dev/plugin-development/how-tos/v12-sdk-migration" /> + </Target> +</Project>