diff --git a/.github/generate_changelog.py b/.github/generate_changelog.py new file mode 100644 index 000000000..5e921fd6e --- /dev/null +++ b/.github/generate_changelog.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +""" +Generate a changelog from git commits between the last two tags and post to Discord webhook. +""" + +import subprocess +import re +import sys +import json +import argparse +from typing import List, Tuple, Optional + + +def run_git_command(args: List[str]) -> str: + """Run a git command and return its output.""" + try: + result = subprocess.run( + ["git"] + args, + capture_output=True, + text=True, + check=True + ) + return result.stdout.strip() + except subprocess.CalledProcessError as e: + print(f"Git command failed: {e}", file=sys.stderr) + sys.exit(1) + + +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] + + +def get_submodule_commit(submodule_path: str, tag: str) -> Optional[str]: + """Get the commit hash of a submodule at a specific tag.""" + try: + # Get the submodule commit at the specified tag + result = run_git_command(["ls-tree", tag, submodule_path]) + # Format is: " commit \t" + parts = result.split() + if len(parts) >= 3 and parts[1] == "commit": + return parts[2] + return None + except: + return None + + +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=%s|%an|%h" + ]) + + 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 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 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, commits: List[Tuple[str, str]], + cs_commit_new: Optional[str], cs_commit_old: Optional[str]) -> str: + """Generate markdown changelog.""" + # Calculate statistics + 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 **{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 message, author, sha in commits: + changelog += f"* {message} (by **{author}** as [`{sha}`]())\n" + + return changelog + + +def post_to_discord(webhook_url: str, content: str, version: str) -> None: + """Post changelog to Discord webhook as a file attachment.""" + try: + import requests + 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!", + "attachments": [ + { + "id": "0", + "filename": filename + } + ] + } + + # 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() + print(f"Successfully posted to Discord webhook, code {result.status_code}") + except requests.exceptions.HTTPError as err: + print(f"Failed to post to Discord: {err}", file=sys.stderr) + sys.exit(1) + except Exception as e: + print(f"Failed to post to Discord: {e}", file=sys.stderr) + sys.exit(1) + + +def main(): + parser = argparse.ArgumentParser( + description="Generate changelog from git commits and post to Discord webhook" + ) + parser.add_argument( + "--webhook-url", + required=True, + help="Discord webhook URL" + ) + parser.add_argument( + "--ignore", + action="append", + default=[], + 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 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 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_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() \ No newline at end of file diff --git a/.github/workflows/backup.yml b/.github/workflows/backup.yml new file mode 100644 index 000000000..366f4682b --- /dev/null +++ b/.github/workflows/backup.yml @@ -0,0 +1,32 @@ +name: Back up code to other forges + +on: + schedule: + - cron: '0 2 * * *' # Run every day at 2 AM + workflow_dispatch: # Allow manual trigger + +jobs: + push-to-forges: + runs-on: ubuntu-latest + if: github.repository == 'goatcorp/Dalamud' + + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd #v0.9.1 + with: + ssh-private-key: | + ${{ secrets.MIRROR_GITLAB_SYNC_KEY }} + ${{ secrets.MIRROR_CODEBERG_SYNC_KEY }} + + - name: Add remotes & push + env: + GIT_SSH_COMMAND: "ssh -o StrictHostKeyChecking=accept-new" + run: | + git remote add gitlab git@gitlab.com:goatcorp/Dalamud.git + git push gitlab --all --force + git remote add codeberg git@codeberg.org:goatcorp/Dalamud.git + git push codeberg --all --force diff --git a/.github/workflows/generate-changelog.yml b/.github/workflows/generate-changelog.yml new file mode 100644 index 000000000..5fed3b1eb --- /dev/null +++ b/.github/workflows/generate-changelog.yml @@ -0,0 +1,46 @@ +name: Generate Changelog + +on: + workflow_dispatch: + push: + tags: + - '*' + +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 + run: | + python .github/generate_changelog.py \ + --webhook-url "${{ secrets.DISCORD_CHANGELOG_WEBHOOK_URL }}" \ + --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 \ No newline at end of file diff --git a/.github/workflows/rollup.yml b/.github/workflows/rollup.yml index 8bc9a3c51..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: @@ -11,7 +11,7 @@ jobs: strategy: matrix: branches: - - net9 + - api14 defaults: run: diff --git a/.github/workflows/update-submodules.yml b/.github/workflows/update-submodules.yml index 19bd86b30..af597fc58 100644 --- a/.github/workflows/update-submodules.yml +++ b/.github/workflows/update-submodules.yml @@ -1,16 +1,26 @@ -name: Check for FFXIVCS changes +name: Check for Submodule Changes on: schedule: - - cron: "0 0,12,18 */1 * *" + - cron: "0 0,6,12,18 * * *" workflow_dispatch: jobs: check: - name: FFXIVCS Check + name: Check ${{ matrix.submodule.name }} runs-on: ubuntu-latest strategy: + fail-fast: false matrix: branches: [master] + submodule: + - name: ClientStructs + path: lib/FFXIVClientStructs + branch: main + branch-prefix: csupdate + - name: Excel Schema + path: lib/Lumina.Excel + branch: master + branch-prefix: schemaupdate defaults: run: @@ -24,30 +34,41 @@ jobs: ref: ${{ matrix.branches }} token: ${{ secrets.UPDATE_PAT }} - name: Create update branch - run: git checkout -b csupdate/${{ matrix.branches }} + run: git checkout -b ${{ matrix.submodule.branch-prefix }}/${{ matrix.branches }} - name: Initialize mandatory git config run: | git config --global user.name "github-actions[bot]" git config --global user.email noreply@github.com git config --global pull.rebase false - name: Update submodule + id: update-submodule run: | - git checkout -b csupdate-${{ matrix.branches }} + git checkout -b ${{ matrix.submodule.branch-prefix }}-${{ matrix.branches }} git reset --hard origin/${{ matrix.branches }} - cd lib/FFXIVClientStructs + cd ${{ matrix.submodule.path }} git fetch - git reset --hard origin/main + git reset --hard origin/${{ matrix.submodule.branch }} cd ../.. - git add lib/FFXIVClientStructs - git commit --message "Update ClientStructs" - git push origin csupdate-${{ matrix.branches }} --force + git add ${{ matrix.submodule.path }} + + if [[ -z "$(git status --porcelain --untracked-files=no)" ]]; then + echo "No changes detected!" + echo "SUBMIT_PR=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + git commit --message "Update ${{ matrix.submodule.name }}" + git push origin ${{ matrix.submodule.branch-prefix }}-${{ matrix.branches }} --force + echo "SUBMIT_PR=true" >> "$GITHUB_OUTPUT" + - name: Create PR + if: ${{ steps.update-submodule.outputs.SUBMIT_PR == 'true' }} run: | echo ${{ secrets.UPDATE_PAT }} | gh auth login --with-token - prNumber=$(gh pr list --base ${{ matrix.branches }} --head csupdate-${{ matrix.branches }} --state open --json number --template "{{range .}}{{.number}}{{end}}") + prNumber=$(gh pr list --base ${{ matrix.branches }} --head ${{ matrix.submodule.branch-prefix }}-${{ matrix.branches }} --state open --json number --template "{{range .}}{{.number}}{{end}}") if [ -z "$prNumber" ]; then echo "No PR found, creating one" - gh pr create --head csupdate-${{ matrix.branches }} --title "[${{ matrix.branches }}] Update ClientStructs" --body "" --base ${{ matrix.branches }} + gh pr create --head ${{ matrix.submodule.branch-prefix }}-${{ matrix.branches }} --title "[${{ matrix.branches }}] Update ${{ matrix.submodule.name }}" --body "" --base ${{ matrix.branches }} else echo "PR already exists, ignoring" fi diff --git a/.gitmodules b/.gitmodules index ba6c43f8e..0d12df2b8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "lib/Hexa.NET.ImGui"] path = lib/Hexa.NET.ImGui url = https://github.com/goatcorp/Hexa.NET.ImGui.git +[submodule "lib/Lumina.Excel"] + path = lib/Lumina.Excel + url = https://github.com/NotAdam/Lumina.Excel.git diff --git a/Dalamud.Boot/Dalamud.Boot.rc b/Dalamud.Boot/Dalamud.Boot.rc index b46e81caf..655df27e1 100644 --- a/Dalamud.Boot/Dalamud.Boot.rc +++ b/Dalamud.Boot/Dalamud.Boot.rc @@ -26,6 +26,38 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US RT_MANIFEST_THEMES RT_MANIFEST "themes.manifest" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_APPNAME "Dalamud Boot" + IDS_MSVCRT_ACTION_OPENDOWNLOAD + "Download Microsoft Visual C++ Redistributable 2022\nExit the game and download the latest setup file from Microsoft." + IDS_MSVCRT_ACTION_IGNORE + "Ignore and Continue\nAttempt to continue with the currently installed version.\nDalamud or plugins may fail to load." + IDS_MSVCRT_DIALOG_MAININSTRUCTION + "Outdated Microsoft Visual C++ Redistributable" + IDS_MSVCRT_DIALOG_CONTENT + "The Microsoft Visual C++ Redistributable version detected on this computer (v{0}.{1}.{2}.{3}) is out of date and may not work with Dalamud." + IDS_MSVCRT_DOWNLOADURL "https://aka.ms/vs/17/release/vc_redist.x64.exe" + IDS_INITIALIZEFAIL_ACTION_ABORT "Abort\nExit the game." + IDS_INITIALIZEFAIL_ACTION_CONTINUE + "Load game without Dalamud\nThe game will launch without Dalamud enabled." + IDS_INITIALIZEFAIL_DIALOG_MAININSTRUCTION "Failed to load Dalamud." + IDS_INITIALIZEFAIL_DIALOG_CONTENT + "An error is preventing Dalamud from being loaded along with the game." +END + +STRINGTABLE +BEGIN + IDS_INITIALIZEFAIL_DIALOG_FOOTER + "Last operation: {0}\nHRESULT: 0x{1:08X}\nDescription: {2}" +END + #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj b/Dalamud.Boot/Dalamud.Boot.vcxproj index a15601af4..0a4a9c563 100644 --- a/Dalamud.Boot/Dalamud.Boot.vcxproj +++ b/Dalamud.Boot/Dalamud.Boot.vcxproj @@ -48,7 +48,7 @@ Level3 true true - stdcpp20 + stdcpp23 pch.h ProgramDatabase CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) @@ -65,7 +65,7 @@ true false - MultiThreadedDebugDLL + MultiThreadedDebugDLL _DEBUG;%(PreprocessorDefinitions) Use 26812 @@ -80,7 +80,7 @@ true true - MultiThreadedDLL + MultiThreadedDLL NDEBUG;%(PreprocessorDefinitions) Use 26812 @@ -133,6 +133,10 @@ NotUsing + + NotUsing + NotUsing + NotUsing @@ -176,6 +180,7 @@ + @@ -206,4 +211,4 @@ - + \ No newline at end of file diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj.filters b/Dalamud.Boot/Dalamud.Boot.vcxproj.filters index 7c26b28ff..15e3eb8b3 100644 --- a/Dalamud.Boot/Dalamud.Boot.vcxproj.filters +++ b/Dalamud.Boot/Dalamud.Boot.vcxproj.filters @@ -76,6 +76,9 @@ Dalamud.Boot DLL + + Common Boot + @@ -146,6 +149,9 @@ Dalamud.Boot DLL + + Common Boot + diff --git a/Dalamud.Boot/dllmain.cpp b/Dalamud.Boot/dllmain.cpp index 5e73962ec..80a16f89a 100644 --- a/Dalamud.Boot/dllmain.cpp +++ b/Dalamud.Boot/dllmain.cpp @@ -9,10 +9,125 @@ #include "utils.h" #include "veh.h" #include "xivfixes.h" +#include "resource.h" HMODULE g_hModule; HINSTANCE g_hGameInstance = GetModuleHandleW(nullptr); +static void CheckMsvcrtVersion() { + // Commit introducing inline mutex ctor: tagged vs-2022-17.14 (2024-06-18) + // - https://github.com/microsoft/STL/commit/22a88260db4d754bbc067e2002430144d6ec5391 + // MSVC Redist versions: + // - https://github.com/abbodi1406/vcredist/blob/master/source_links/README.md + // - 14.40.33810.0 dsig 2024-04-28 + // - 14.40.33816.0 dsig 2024-09-11 + + constexpr WORD RequiredMsvcrtVersionComponents[] = {14, 40, 33816, 0}; + constexpr auto RequiredMsvcrtVersion = 0ULL + | (static_cast(RequiredMsvcrtVersionComponents[0]) << 48) + | (static_cast(RequiredMsvcrtVersionComponents[1]) << 32) + | (static_cast(RequiredMsvcrtVersionComponents[2]) << 16) + | (static_cast(RequiredMsvcrtVersionComponents[3]) << 0); + + constexpr const wchar_t* RuntimeDllNames[] = { +#ifdef _DEBUG + L"msvcp140d.dll", + L"vcruntime140d.dll", + L"vcruntime140_1d.dll", +#else + L"msvcp140.dll", + L"vcruntime140.dll", + L"vcruntime140_1.dll", +#endif + }; + + uint64_t lowestVersion = 0; + for (const auto& runtimeDllName : RuntimeDllNames) { + const utils::loaded_module mod(GetModuleHandleW(runtimeDllName)); + if (!mod) { + logging::E("MSVCRT DLL not found: {}", runtimeDllName); + continue; + } + + const auto path = mod.path() + .transform([](const auto& p) { return p.wstring(); }) + .value_or(runtimeDllName); + + if (const auto versionResult = mod.get_file_version()) { + const auto& versionFull = versionResult->get(); + logging::I("MSVCRT DLL {} has version {}.", path, utils::format_file_version(versionFull)); + + const auto version = 0ULL | + (static_cast(versionFull.dwFileVersionMS) << 32) | + (static_cast(versionFull.dwFileVersionLS) << 0); + + if (version < RequiredMsvcrtVersion && (lowestVersion == 0 || lowestVersion > version)) + lowestVersion = version; + } else { + logging::E("Failed to detect MSVCRT DLL version for {}: {}", path, versionResult.error().describe()); + } + } + + if (!lowestVersion) + return; + + enum IdTaskDialogAction { + IdTaskDialogActionOpenDownload = 101, + IdTaskDialogActionIgnore, + }; + + const TASKDIALOG_BUTTON buttons[]{ + {IdTaskDialogActionOpenDownload, MAKEINTRESOURCEW(IDS_MSVCRT_ACTION_OPENDOWNLOAD)}, + {IdTaskDialogActionIgnore, MAKEINTRESOURCEW(IDS_MSVCRT_ACTION_IGNORE)}, + }; + + const WORD lowestVersionComponents[]{ + static_cast(lowestVersion >> 48), + static_cast(lowestVersion >> 32), + static_cast(lowestVersion >> 16), + static_cast(lowestVersion >> 0), + }; + + const auto dialogContent = std::vformat( + utils::get_string_resource(IDS_MSVCRT_DIALOG_CONTENT), + std::make_wformat_args( + lowestVersionComponents[0], + lowestVersionComponents[1], + lowestVersionComponents[2], + lowestVersionComponents[3])); + + const TASKDIALOGCONFIG config{ + .cbSize = sizeof config, + .hInstance = g_hModule, + .dwFlags = TDF_CAN_BE_MINIMIZED | TDF_ALLOW_DIALOG_CANCELLATION | TDF_USE_COMMAND_LINKS, + .pszWindowTitle = MAKEINTRESOURCEW(IDS_APPNAME), + .pszMainIcon = MAKEINTRESOURCEW(IDI_ICON1), + .pszMainInstruction = MAKEINTRESOURCEW(IDS_MSVCRT_DIALOG_MAININSTRUCTION), + .pszContent = dialogContent.c_str(), + .cButtons = _countof(buttons), + .pButtons = buttons, + .nDefaultButton = IdTaskDialogActionOpenDownload, + }; + + int buttonPressed; + if (utils::scoped_dpi_awareness_context ctx; + FAILED(TaskDialogIndirect(&config, &buttonPressed, nullptr, nullptr))) + buttonPressed = IdTaskDialogActionOpenDownload; + + switch (buttonPressed) { + case IdTaskDialogActionOpenDownload: + ShellExecuteW( + nullptr, + L"open", + utils::get_string_resource(IDS_MSVCRT_DOWNLOADURL).c_str(), + nullptr, + nullptr, + SW_SHOW); + ExitProcess(0); + break; + } +} + HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) { g_startInfo.from_envvars(); @@ -24,7 +139,7 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) { } if (g_startInfo.BootShowConsole) - ConsoleSetup(L"Dalamud Boot"); + ConsoleSetup(utils::get_string_resource(IDS_APPNAME).c_str()); logging::update_dll_load_status(true); @@ -94,6 +209,8 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) { if ((g_startInfo.BootWaitMessageBox & DalamudStartInfo::WaitMessageboxFlags::BeforeInitialize) != DalamudStartInfo::WaitMessageboxFlags::None) MessageBoxW(nullptr, L"Press OK to continue (BeforeInitialize)", L"Dalamud Boot", MB_OK); + CheckMsvcrtVersion(); + if (g_startInfo.BootDebugDirectX) { logging::I("Enabling DirectX Debugging."); @@ -159,7 +276,7 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) { if (minHookLoaded) { logging::I("Applying fixes..."); - xivfixes::apply_all(true); + std::thread([] { xivfixes::apply_all(true); }).join(); logging::I("Fixes OK"); } else { logging::W("Skipping fixes, as MinHook has failed to load."); @@ -170,11 +287,14 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) { while (!IsDebuggerPresent()) Sleep(100); logging::I("Debugger attached."); + __debugbreak(); } - const auto fs_module_path = utils::get_module_path(g_hModule); - const auto runtimeconfig_path = std::filesystem::path(fs_module_path).replace_filename(L"Dalamud.runtimeconfig.json").wstring(); - const auto module_path = std::filesystem::path(fs_module_path).replace_filename(L"Dalamud.dll").wstring(); + const auto fs_module_path = utils::loaded_module(g_hModule).path(); + if (!fs_module_path) + return fs_module_path.error(); + const auto runtimeconfig_path = std::filesystem::path(*fs_module_path).replace_filename(L"Dalamud.runtimeconfig.json").wstring(); + const auto module_path = std::filesystem::path(*fs_module_path).replace_filename(L"Dalamud.dll").wstring(); // ============================== CLR ========================================= // diff --git a/Dalamud.Boot/error_info.cpp b/Dalamud.Boot/error_info.cpp new file mode 100644 index 000000000..02356b730 --- /dev/null +++ b/Dalamud.Boot/error_info.cpp @@ -0,0 +1,26 @@ +#include "error_info.h" + +#define WIN32_LEAN_AND_MEAN +#include + +DalamudBootError::DalamudBootError(DalamudBootErrorDescription dalamudErrorDescription, long hresult) noexcept + : m_dalamudErrorDescription(dalamudErrorDescription) + , m_hresult(hresult) { +} + +DalamudBootError::DalamudBootError(DalamudBootErrorDescription dalamudErrorDescription) noexcept + : DalamudBootError(dalamudErrorDescription, E_FAIL) { +} + +const char* DalamudBootError::describe() const { + switch (m_dalamudErrorDescription) { + case DalamudBootErrorDescription::ModuleResourceLoadFail: + return "Failed to load resource."; + case DalamudBootErrorDescription::ModuleResourceVersionReadFail: + return "Failed to query version information."; + case DalamudBootErrorDescription::ModuleResourceVersionSignatureFail: + return "Invalid version info found."; + default: + return "(unavailable)"; + } +} diff --git a/Dalamud.Boot/error_info.h b/Dalamud.Boot/error_info.h new file mode 100644 index 000000000..b5862d0dd --- /dev/null +++ b/Dalamud.Boot/error_info.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +typedef unsigned long DWORD; +typedef _Return_type_success_(return >= 0) long HRESULT; + +enum class DalamudBootErrorDescription { + None, + ModulePathResolutionFail, + ModuleResourceLoadFail, + ModuleResourceVersionReadFail, + ModuleResourceVersionSignatureFail, +}; + +class DalamudBootError { + DalamudBootErrorDescription m_dalamudErrorDescription; + long m_hresult; + +public: + DalamudBootError(DalamudBootErrorDescription dalamudErrorDescription, long hresult) noexcept; + DalamudBootError(DalamudBootErrorDescription dalamudErrorDescription) noexcept; + + const char* describe() const; + + operator HRESULT() const { + return m_hresult; + } +}; + +template +using DalamudExpected = std::expected< + std::conditional_t< + std::is_reference_v, + std::reference_wrapper>, + T + >, + DalamudBootError +>; + +using DalamudUnexpected = std::unexpected; diff --git a/Dalamud.Boot/hooks.cpp b/Dalamud.Boot/hooks.cpp index 295d427ae..3443a5f8a 100644 --- a/Dalamud.Boot/hooks.cpp +++ b/Dalamud.Boot/hooks.cpp @@ -84,19 +84,13 @@ void hooks::getprocaddress_singleton_import_hook::initialize() { const auto dllName = unicode::convert(pData->Loaded.FullDllName->Buffer); utils::loaded_module mod(pData->Loaded.DllBase); - std::wstring version, description; - try { - version = utils::format_file_version(mod.get_file_version()); - } catch (...) { - version = L""; - } - - try { - description = mod.get_description(); - } catch (...) { - description = L""; - } - + const auto version = mod.get_file_version() + .transform([](const auto& v) { return utils::format_file_version(v.get()); }) + .value_or(L""); + + const auto description = mod.get_description() + .value_or(L""); + logging::I(R"({} "{}" ("{}" ver {}) has been loaded at 0x{:X} ~ 0x{:X} (0x{:X}); finding import table items to hook.)", LogTag, dllName, description, version, reinterpret_cast(pData->Loaded.DllBase), @@ -125,7 +119,9 @@ void hooks::getprocaddress_singleton_import_hook::hook_module(const utils::loade if (mod.is_current_process()) return; - const auto path = unicode::convert(mod.path().wstring()); + const auto path = mod.path() + .transform([](const auto& p) { return unicode::convert(p.wstring()); }) + .value_or(""); for (const auto& [hModule, targetFns] : m_targetFns) { for (const auto& [targetFn, pfnThunk] : targetFns) { @@ -133,7 +129,7 @@ void hooks::getprocaddress_singleton_import_hook::hook_module(const utils::loade if (void* pGetProcAddressImport; mod.find_imported_function_pointer(dllName.c_str(), targetFn.c_str(), 0, pGetProcAddressImport)) { auto& hook = m_hooks[hModule][targetFn][mod]; if (!hook) { - logging::I("{} Hooking {}!{} imported by {}", LogTag, dllName, targetFn, unicode::convert(mod.path().wstring())); + logging::I("{} Hooking {}!{} imported by {}", LogTag, dllName, targetFn, path); hook.emplace(std::format("getprocaddress_singleton_import_hook::hook_module({}!{})", dllName, targetFn), static_cast(pGetProcAddressImport), pfnThunk); } diff --git a/Dalamud.Boot/pch.h b/Dalamud.Boot/pch.h index c2194c157..ac1394e57 100644 --- a/Dalamud.Boot/pch.h +++ b/Dalamud.Boot/pch.h @@ -11,6 +11,9 @@ #define WIN32_LEAN_AND_MEAN #define NOMINMAX +// https://developercommunity.visualstudio.com/t/Access-violation-with-std::mutex::lock-a/10664660 +#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR + // Windows Header Files (1) #include @@ -21,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +55,7 @@ #include #include #include +#include #include // https://www.akenotsuki.com/misc/srell/en/ diff --git a/Dalamud.Boot/resource.h b/Dalamud.Boot/resource.h index 51acf37df..2a1cde6e2 100644 --- a/Dalamud.Boot/resource.h +++ b/Dalamud.Boot/resource.h @@ -3,12 +3,23 @@ // Used by Dalamud.Boot.rc // #define IDI_ICON1 101 +#define IDS_APPNAME 102 +#define IDS_MSVCRT_ACTION_OPENDOWNLOAD 103 +#define IDS_MSVCRT_ACTION_IGNORE 104 +#define IDS_MSVCRT_DIALOG_MAININSTRUCTION 105 +#define IDS_MSVCRT_DIALOG_CONTENT 106 +#define IDS_MSVCRT_DOWNLOADURL 107 +#define IDS_INITIALIZEFAIL_ACTION_ABORT 108 +#define IDS_INITIALIZEFAIL_ACTION_CONTINUE 109 +#define IDS_INITIALIZEFAIL_DIALOG_MAININSTRUCTION 110 +#define IDS_INITIALIZEFAIL_DIALOG_CONTENT 111 +#define IDS_INITIALIZEFAIL_DIALOG_FOOTER 112 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/Dalamud.Boot/rewrite_entrypoint.cpp b/Dalamud.Boot/rewrite_entrypoint.cpp index 3a1672af7..69a4ec818 100644 --- a/Dalamud.Boot/rewrite_entrypoint.cpp +++ b/Dalamud.Boot/rewrite_entrypoint.cpp @@ -2,6 +2,7 @@ #include "logging.h" #include "utils.h" +#include "resource.h" HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue); @@ -379,12 +380,50 @@ extern "C" void WINAPI RewrittenEntryPoint_AdjustedStack(RewrittenEntryPointPara auto desc = err.Description(); if (desc.length() == 0) desc = err.ErrorMessage(); - if (MessageBoxW(nullptr, std::format( - L"Failed to load Dalamud. Load game without Dalamud(yes) or abort(no)?\n\n{}\n{}", - last_operation, - desc.GetBSTR()).c_str(), - L"Dalamud.Boot", MB_OK | MB_YESNO) == IDNO) - ExitProcess(-1); + + enum IdTaskDialogAction { + IdTaskDialogActionAbort = 101, + IdTaskDialogActionContinue, + }; + + const TASKDIALOG_BUTTON buttons[]{ + {IdTaskDialogActionAbort, MAKEINTRESOURCEW(IDS_INITIALIZEFAIL_ACTION_ABORT)}, + {IdTaskDialogActionContinue, MAKEINTRESOURCEW(IDS_INITIALIZEFAIL_ACTION_CONTINUE)}, + }; + + const auto hru32 = static_cast(hr); + const auto footer = std::vformat( + utils::get_string_resource(IDS_INITIALIZEFAIL_DIALOG_FOOTER), + std::make_wformat_args( + last_operation, + hru32, + desc.GetBSTR())); + + const TASKDIALOGCONFIG config{ + .cbSize = sizeof config, + .hInstance = g_hModule, + .dwFlags = TDF_CAN_BE_MINIMIZED | TDF_ALLOW_DIALOG_CANCELLATION | TDF_USE_COMMAND_LINKS | TDF_EXPAND_FOOTER_AREA, + .pszWindowTitle = MAKEINTRESOURCEW(IDS_APPNAME), + .pszMainIcon = MAKEINTRESOURCEW(IDI_ICON1), + .pszMainInstruction = MAKEINTRESOURCEW(IDS_INITIALIZEFAIL_DIALOG_MAININSTRUCTION), + .pszContent = MAKEINTRESOURCEW(IDS_INITIALIZEFAIL_DIALOG_CONTENT), + .cButtons = _countof(buttons), + .pButtons = buttons, + .nDefaultButton = IdTaskDialogActionAbort, + .pszFooter = footer.c_str(), + }; + + int buttonPressed; + if (utils::scoped_dpi_awareness_context ctx; + FAILED(TaskDialogIndirect(&config, &buttonPressed, nullptr, nullptr))) + buttonPressed = IdTaskDialogActionAbort; + + switch (buttonPressed) { + case IdTaskDialogActionAbort: + ExitProcess(-1); + break; + } + if (hMainThreadContinue) { CloseHandle(hMainThreadContinue); hMainThreadContinue = nullptr; diff --git a/Dalamud.Boot/utils.cpp b/Dalamud.Boot/utils.cpp index dbfcf39ee..9820e5b7f 100644 --- a/Dalamud.Boot/utils.cpp +++ b/Dalamud.Boot/utils.cpp @@ -3,22 +3,27 @@ #include "utils.h" -std::filesystem::path utils::loaded_module::path() const { - std::wstring buf(MAX_PATH, L'\0'); - for (;;) { - if (const auto len = GetModuleFileNameExW(GetCurrentProcess(), m_hModule, &buf[0], static_cast(buf.size())); len != buf.size()) { - if (buf.empty()) - throw std::runtime_error(std::format("Failed to resolve module path: Win32 error {}", GetLastError())); +DalamudExpected utils::loaded_module::path() const { + for (std::wstring buf(MAX_PATH, L'\0');; buf.resize(buf.size() * 2)) { + if (const auto len = GetModuleFileNameW(m_hModule, &buf[0], static_cast(buf.size())); + len != buf.size()) { + if (!len) { + return DalamudUnexpected( + std::in_place, + DalamudBootErrorDescription::ModulePathResolutionFail, + HRESULT_FROM_WIN32(GetLastError())); + } + buf.resize(len); return buf; } - if (buf.size() * 2 < PATHCCH_MAX_CCH) - buf.resize(buf.size() * 2); - else if (auto p = std::filesystem::path(buf); exists(p)) - return p; - else - throw std::runtime_error("Failed to resolve module path: no amount of buffer size would fit the data"); + if (buf.size() > PATHCCH_MAX_CCH) { + return DalamudUnexpected( + std::in_place, + DalamudBootErrorDescription::ModulePathResolutionFail, + E_OUTOFMEMORY); + } } } @@ -144,66 +149,90 @@ void* utils::loaded_module::get_imported_function_pointer(const char* pcszDllNam throw std::runtime_error(std::format("Failed to find import for {}!{} ({}).", pcszDllName, pcszFunctionName ? pcszFunctionName : "", hintOrOrdinal)); } -std::unique_ptr, decltype(&FreeResource)> utils::loaded_module::get_resource(LPCWSTR lpName, LPCWSTR lpType) const { +DalamudExpected, decltype(&FreeResource)>> utils::loaded_module::get_resource(LPCWSTR lpName, LPCWSTR lpType) const { const auto hres = FindResourceW(m_hModule, lpName, lpType); if (!hres) - throw std::runtime_error("No such resource"); - + return DalamudUnexpected(std::in_place, DalamudBootErrorDescription::ModuleResourceLoadFail, GetLastError()); + const auto hRes = LoadResource(m_hModule, hres); if (!hRes) - throw std::runtime_error("LoadResource failure"); + return DalamudUnexpected(std::in_place, DalamudBootErrorDescription::ModuleResourceLoadFail, GetLastError()); - return {hRes, &FreeResource}; + return std::unique_ptr, decltype(&FreeResource)>(hRes, &FreeResource); } -std::wstring utils::loaded_module::get_description() const { - const auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); - const auto pBlock = LockResource(rsrc.get()); - +DalamudExpected utils::loaded_module::get_description() const { + auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); + if (!rsrc) + return DalamudUnexpected(std::move(rsrc.error())); + + const auto pBlock = LockResource(rsrc->get()); + struct LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; } * lpTranslate; UINT cbTranslate; if (!VerQueryValueW(pBlock, - TEXT("\\VarFileInfo\\Translation"), + L"\\VarFileInfo\\Translation", reinterpret_cast(&lpTranslate), &cbTranslate)) { - throw std::runtime_error("Invalid version information (1)"); + return DalamudUnexpected( + std::in_place, + DalamudBootErrorDescription::ModuleResourceVersionReadFail, + HRESULT_FROM_WIN32(GetLastError())); } for (size_t i = 0; i < (cbTranslate / sizeof(LANGANDCODEPAGE)); i++) { + wchar_t subblockNameBuf[64]; + *std::format_to_n( + subblockNameBuf, + _countof(subblockNameBuf), + L"\\StringFileInfo\\{:04x}{:04x}\\FileDescription", + lpTranslate[i].wLanguage, + lpTranslate[i].wCodePage).out = 0;; + wchar_t* buf = nullptr; UINT size = 0; - if (!VerQueryValueW(pBlock, - std::format(L"\\StringFileInfo\\{:04x}{:04x}\\FileDescription", - lpTranslate[i].wLanguage, - lpTranslate[i].wCodePage).c_str(), - reinterpret_cast(&buf), - &size)) { + if (!VerQueryValueW(pBlock, subblockNameBuf, reinterpret_cast(&buf), &size)) continue; - } + auto currName = std::wstring_view(buf, size); - while (!currName.empty() && currName.back() == L'\0') - currName = currName.substr(0, currName.size() - 1); + if (const auto p = currName.find(L'\0'); p != std::string::npos) + currName = currName.substr(0, p); if (currName.empty()) continue; return std::wstring(currName); } - - throw std::runtime_error("Invalid version information (2)"); + + return DalamudUnexpected( + std::in_place, + DalamudBootErrorDescription::ModuleResourceVersionReadFail, + HRESULT_FROM_WIN32(ERROR_NOT_FOUND)); } -VS_FIXEDFILEINFO utils::loaded_module::get_file_version() const { - const auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); - const auto pBlock = LockResource(rsrc.get()); +std::expected, DalamudBootError> utils::loaded_module::get_file_version() const { + auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); + if (!rsrc) + return DalamudUnexpected(std::move(rsrc.error())); + + const auto pBlock = LockResource(rsrc->get()); UINT size = 0; LPVOID lpBuffer = nullptr; - if (!VerQueryValueW(pBlock, L"\\", &lpBuffer, &size)) - throw std::runtime_error("Failed to query version information."); + if (!VerQueryValueW(pBlock, L"\\", &lpBuffer, &size)) { + return std::unexpected( + std::in_place, + DalamudBootErrorDescription::ModuleResourceVersionReadFail, + HRESULT_FROM_WIN32(GetLastError())); + } + const VS_FIXEDFILEINFO& versionInfo = *static_cast(lpBuffer); - if (versionInfo.dwSignature != 0xfeef04bd) - throw std::runtime_error("Invalid version info found."); + if (versionInfo.dwSignature != 0xfeef04bd) { + return std::unexpected( + std::in_place, + DalamudBootErrorDescription::ModuleResourceVersionSignatureFail); + } + return versionInfo; } @@ -353,7 +382,7 @@ const char* utils::signature_finder::result::resolve_jump_target(size_t instruct nmd_x86_instruction instruction{}; if (!nmd_x86_decode(&Match[instructionOffset], NMD_X86_MAXIMUM_INSTRUCTION_LENGTH, &instruction, NMD_X86_MODE_64, NMD_X86_DECODER_FLAGS_ALL)) throw std::runtime_error("Matched address does not have a valid assembly instruction"); - + size_t numExplicitOperands = 0; for (size_t i = 0; i < instruction.num_operands; i++) numExplicitOperands += instruction.operands[i].is_implicit ? 0 : 1; @@ -589,17 +618,10 @@ bool utils::is_running_on_wine() { return g_startInfo.Platform != "WINDOWS"; } -std::filesystem::path utils::get_module_path(HMODULE hModule) { - std::wstring buf(MAX_PATH, L'\0'); - while (true) { - if (const auto res = GetModuleFileNameW(hModule, &buf[0], static_cast(buf.size())); !res) - throw std::runtime_error(std::format("GetModuleFileName failure: 0x{:X}", GetLastError())); - else if (res < buf.size()) { - buf.resize(res); - return buf; - } else - buf.resize(buf.size() * 2); - } +std::wstring utils::get_string_resource(uint32_t resId) { + LPCWSTR pstr; + const auto len = LoadStringW(g_hModule, resId, reinterpret_cast(&pstr), 0); + return std::wstring(pstr, len); } HWND utils::try_find_game_window() { @@ -625,7 +647,7 @@ void utils::wait_for_game_window() { std::wstring utils::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); @@ -677,3 +699,22 @@ std::wstring utils::format_win32_error(DWORD err) { return std::format(L"Win32 error ({}=0x{:X})", err, err); } + +utils::scoped_dpi_awareness_context::scoped_dpi_awareness_context() + : scoped_dpi_awareness_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) { +} + +utils::scoped_dpi_awareness_context::scoped_dpi_awareness_context(DPI_AWARENESS_CONTEXT context) { + const auto user32 = GetModuleHandleW(L"user32.dll"); + m_setThreadDpiAwarenessContext = + user32 + ? reinterpret_cast( + GetProcAddress(user32, "SetThreadDpiAwarenessContext")) + : nullptr; + m_old = m_setThreadDpiAwarenessContext ? m_setThreadDpiAwarenessContext(context) : DPI_AWARENESS_CONTEXT_UNAWARE; +} + +utils::scoped_dpi_awareness_context::~scoped_dpi_awareness_context() { + if (m_setThreadDpiAwarenessContext) + m_setThreadDpiAwarenessContext(m_old); +} diff --git a/Dalamud.Boot/utils.h b/Dalamud.Boot/utils.h index 2cdaf60a7..c5833722b 100644 --- a/Dalamud.Boot/utils.h +++ b/Dalamud.Boot/utils.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -7,6 +8,7 @@ #include #include +#include "error_info.h" #include "unicode.h" namespace utils { @@ -18,14 +20,13 @@ namespace utils { loaded_module(void* hModule) : m_hModule(reinterpret_cast(hModule)) {} loaded_module(size_t hModule) : m_hModule(reinterpret_cast(hModule)) {} - std::filesystem::path path() const; + DalamudExpected path() const; bool is_current_process() const { return m_hModule == GetModuleHandleW(nullptr); } bool owns_address(const void* pAddress) const; - operator HMODULE() const { - return m_hModule; - } + operator HMODULE() const { return m_hModule; } + operator bool() const { return m_hModule; } size_t address_int() const { return reinterpret_cast(m_hModule); } size_t image_size() const { return is_pe64() ? nt_header64().OptionalHeader.SizeOfImage : nt_header32().OptionalHeader.SizeOfImage; } @@ -58,9 +59,9 @@ namespace utils { void* get_imported_function_pointer(const char* pcszDllName, const char* pcszFunctionName, uint32_t hintOrOrdinal) const; template TFn** get_imported_function_pointer(const char* pcszDllName, const char* pcszFunctionName, uint32_t hintOrOrdinal) { return reinterpret_cast(get_imported_function_pointer(pcszDllName, pcszFunctionName, hintOrOrdinal)); } - [[nodiscard]] std::unique_ptr, decltype(&FreeResource)> get_resource(LPCWSTR lpName, LPCWSTR lpType) const; - [[nodiscard]] std::wstring get_description() const; - [[nodiscard]] VS_FIXEDFILEINFO get_file_version() const; + [[nodiscard]] DalamudExpected, decltype(&FreeResource)>> get_resource(LPCWSTR lpName, LPCWSTR lpType) const; + [[nodiscard]] DalamudExpected get_description() const; + [[nodiscard]] DalamudExpected get_file_version() const; static loaded_module current_process(); static std::vector all_modules(); @@ -269,7 +270,7 @@ namespace utils { bool is_running_on_wine(); - std::filesystem::path get_module_path(HMODULE hModule); + std::wstring get_string_resource(uint32_t resId); /// @brief Find the game main window. /// @return Handle to the game main window, or nullptr if it doesn't exist (yet). @@ -280,4 +281,18 @@ namespace utils { std::wstring escape_shell_arg(const std::wstring& arg); std::wstring format_win32_error(DWORD err); + + class scoped_dpi_awareness_context { + DPI_AWARENESS_CONTEXT m_old; + decltype(&SetThreadDpiAwarenessContext) m_setThreadDpiAwarenessContext; + + public: + scoped_dpi_awareness_context(); + scoped_dpi_awareness_context(DPI_AWARENESS_CONTEXT); + ~scoped_dpi_awareness_context(); + scoped_dpi_awareness_context(const scoped_dpi_awareness_context&) = delete; + scoped_dpi_awareness_context(scoped_dpi_awareness_context&&) = delete; + scoped_dpi_awareness_context& operator=(const scoped_dpi_awareness_context&) = delete; + scoped_dpi_awareness_context& operator=(scoped_dpi_awareness_context&&) = delete; + }; } diff --git a/Dalamud.Boot/veh.cpp b/Dalamud.Boot/veh.cpp index 85d58eb9d..b0ec1cefa 100644 --- a/Dalamud.Boot/veh.cpp +++ b/Dalamud.Boot/veh.cpp @@ -102,9 +102,13 @@ bool is_ffxiv_address(const wchar_t* module_name, const DWORD64 address) return false; } -static void append_injector_launch_args(std::vector& args) +static DalamudExpected append_injector_launch_args(std::vector& args) { - args.emplace_back(L"--game=\"" + utils::loaded_module::current_process().path().wstring() + L"\""); + if (auto path = utils::loaded_module::current_process().path()) + args.emplace_back(L"--game=\"" + path->wstring() + L"\""); + else + return DalamudUnexpected(std::in_place, std::move(path.error())); + switch (g_startInfo.DalamudLoadMethod) { case DalamudStartInfo::LoadMethod::Entrypoint: args.emplace_back(L"--mode=entrypoint"); @@ -155,6 +159,8 @@ static void append_injector_launch_args(std::vector& args) args.emplace_back(szArgList[i]); LocalFree(szArgList); } + + return {}; } LONG exception_handler(EXCEPTION_POINTERS* ex) @@ -358,11 +364,20 @@ bool veh::add_handler(bool doFullDump, const std::string& workingDirectory) args.emplace_back(std::format(L"--process-handle={}", reinterpret_cast(hInheritableCurrentProcess))); args.emplace_back(std::format(L"--exception-info-pipe-read-handle={}", reinterpret_cast(hReadPipeInheritable->get()))); args.emplace_back(std::format(L"--asset-directory={}", unicode::convert(g_startInfo.AssetDirectory))); - args.emplace_back(std::format(L"--log-directory={}", g_startInfo.BootLogPath.empty() - ? utils::loaded_module(g_hModule).path().parent_path().wstring() - : std::filesystem::path(unicode::convert(g_startInfo.BootLogPath)).parent_path().wstring())); + if (const auto path = utils::loaded_module(g_hModule).path()) { + args.emplace_back(std::format(L"--log-directory={}", g_startInfo.BootLogPath.empty() + ? path->parent_path().wstring() + : std::filesystem::path(unicode::convert(g_startInfo.BootLogPath)).parent_path().wstring())); + } else { + logging::W("Failed to read path of the Dalamud Boot module: {}", path.error().describe()); + return false; + } + args.emplace_back(L"--"); - append_injector_launch_args(args); + if (auto r = append_injector_launch_args(args); !r) { + logging::W("Failed to generate injector launch args: {}", r.error().describe()); + return false; + } for (const auto& arg : args) { diff --git a/Dalamud.Boot/xivfixes.cpp b/Dalamud.Boot/xivfixes.cpp index eb0f7df56..7f9e92225 100644 --- a/Dalamud.Boot/xivfixes.cpp +++ b/Dalamud.Boot/xivfixes.cpp @@ -8,12 +8,6 @@ #include "ntdll.h" #include "utils.h" -template -static std::span assume_nonempty_span(std::span t, const char* descr) { - if (t.empty()) - throw std::runtime_error(std::format("Unexpected empty span found: {}", descr)); - return t; -} void xivfixes::unhook_dll(bool bApply) { static const auto LogTag = "[xivfixes:unhook_dll]"; static const auto LogTagW = L"[xivfixes:unhook_dll]"; @@ -23,77 +17,90 @@ void xivfixes::unhook_dll(bool bApply) { const auto mods = utils::loaded_module::all_modules(); - const auto test_module = [&](size_t i, const utils::loaded_module & mod) { - std::filesystem::path path; - try { - path = mod.path(); - std::wstring version, description; - try { - version = utils::format_file_version(mod.get_file_version()); - } catch (...) { - version = L""; - } - - try { - description = mod.get_description(); - } catch (...) { - description = L""; - } - - logging::I(R"({} [{}/{}] Module 0x{:X} ~ 0x{:X} (0x{:X}): "{}" ("{}" ver {}))", LogTagW, i + 1, mods.size(), mod.address_int(), mod.address_int() + mod.image_size(), mod.image_size(), path.wstring(), description, version); - } catch (const std::exception& e) { - logging::W("{} [{}/{}] Module 0x{:X}: Failed to resolve path: {}", LogTag, i + 1, mods.size(), mod.address_int(), e.what()); + for (size_t i = 0; i < mods.size(); i++) { + const auto& mod = mods[i]; + const auto path = mod.path(); + if (!path) { + logging::W( + "{} [{}/{}] Module 0x{:X}: Failed to resolve path: {}", + LogTag, + i + 1, + mods.size(), + mod.address_int(), + path.error().describe()); return; } - const auto moduleName = unicode::convert(path.filename().wstring()); + const auto version = mod.get_file_version() + .transform([](const auto& v) { return utils::format_file_version(v.get()); }) + .value_or(L""); - std::vector buf; - std::string formatBuf; + const auto description = mod.get_description() + .value_or(L""); + + logging::I( + R"({} [{}/{}] Module 0x{:X} ~ 0x{:X} (0x{:X}): "{}" ("{}" ver {}))", + LogTagW, + i + 1, + mods.size(), + mod.address_int(), + mod.address_int() + mod.image_size(), + mod.image_size(), + path->wstring(), + description, + version); + + const auto moduleName = unicode::convert(path->filename().wstring()); + + const auto& sectionHeader = mod.section_header(".text"); + const auto section = mod.span_as(sectionHeader.VirtualAddress, sectionHeader.Misc.VirtualSize); + if (section.empty()) { + logging::W("{} Error: .text[VA:VA + VS] is empty", LogTag); + return; + } + + auto hFsDllRaw = CreateFileW(path->c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); + if (hFsDllRaw == INVALID_HANDLE_VALUE) { + logging::W("{} Module loaded in current process but could not open file: Win32 error {}", LogTag, GetLastError()); + return; + } + + auto hFsDll = std::unique_ptr(hFsDllRaw, &CloseHandle); + std::vector buf(section.size()); + SetFilePointer(hFsDll.get(), sectionHeader.PointerToRawData, nullptr, FILE_CURRENT); + if (DWORD read{}; ReadFile(hFsDll.get(), &buf[0], static_cast(buf.size()), &read, nullptr)) { + if (read < section.size_bytes()) { + logging::W("{} ReadFile: read {} bytes < requested {} bytes", LogTagW, read, section.size_bytes()); + return; + } + } else { + logging::I("{} ReadFile: Win32 error {}", LogTagW, GetLastError()); + return; + } + + const auto doRestore = g_startInfo.BootUnhookDlls.contains(unicode::convert(path->filename().u8string())); try { - const auto& sectionHeader = mod.section_header(".text"); - const auto section = assume_nonempty_span(mod.span_as(sectionHeader.VirtualAddress, sectionHeader.Misc.VirtualSize), ".text[VA:VA+VS]"); - auto hFsDllRaw = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); - if (hFsDllRaw == INVALID_HANDLE_VALUE) { - logging::W("{} Module loaded in current process but could not open file: Win32 error {}", LogTag, GetLastError()); - return; - } - auto hFsDll = std::unique_ptr(hFsDllRaw, &CloseHandle); - - buf.resize(section.size()); - SetFilePointer(hFsDll.get(), sectionHeader.PointerToRawData, nullptr, FILE_CURRENT); - if (DWORD read{}; ReadFile(hFsDll.get(), &buf[0], static_cast(buf.size()), &read, nullptr)) { - if (read < section.size_bytes()) { - logging::W("{} ReadFile: read {} bytes < requested {} bytes", LogTagW, read, section.size_bytes()); - return; - } - } else { - logging::I("{} ReadFile: Win32 error {}", LogTagW, GetLastError()); - return; - } - - const auto doRestore = g_startInfo.BootUnhookDlls.contains(unicode::convert(path.filename().u8string())); - std::optional tenderizer; - for (size_t i = 0, instructionLength = 1, printed = 0; i < buf.size(); i += instructionLength) { - if (section[i] == buf[i]) { + std::string formatBuf; + for (size_t inst = 0, instructionLength = 1, printed = 0; inst < buf.size(); inst += instructionLength) { + if (section[inst] == buf[inst]) { instructionLength = 1; continue; } - const auto rva = sectionHeader.VirtualAddress + i; + const auto rva = sectionHeader.VirtualAddress + inst; nmd_x86_instruction instruction{}; - if (!nmd_x86_decode(§ion[i], section.size() - i, &instruction, NMD_X86_MODE_64, NMD_X86_DECODER_FLAGS_ALL)) { + if (!nmd_x86_decode(§ion[inst], section.size() - inst, &instruction, NMD_X86_MODE_64, NMD_X86_DECODER_FLAGS_ALL)) { instructionLength = 1; if (printed < 64) { - logging::W("{} {}+0x{:0X}: dd {:02X}", LogTag, moduleName, rva, static_cast(section[i])); + logging::W("{} {}+0x{:0X}: dd {:02X}", LogTag, moduleName, rva, static_cast(section[inst])); printed++; } } else { instructionLength = instruction.length; if (printed < 64) { formatBuf.resize(128); - nmd_x86_format(&instruction, &formatBuf[0], reinterpret_cast(§ion[i]), NMD_X86_FORMAT_FLAGS_DEFAULT | NMD_X86_FORMAT_FLAGS_BYTES); + nmd_x86_format(&instruction, &formatBuf[0], reinterpret_cast(§ion[inst]), NMD_X86_FORMAT_FLAGS_DEFAULT | NMD_X86_FORMAT_FLAGS_BYTES); formatBuf.resize(strnlen(&formatBuf[0], formatBuf.size())); const auto& directory = mod.data_directory(IMAGE_DIRECTORY_ENTRY_EXPORT); @@ -103,25 +110,25 @@ void xivfixes::unhook_dll(bool bApply) { const auto functions = mod.span_as(exportDirectory.AddressOfFunctions, exportDirectory.NumberOfFunctions); std::string resolvedExportName; - for (size_t j = 0; j < names.size(); ++j) { + for (size_t nameIndex = 0; nameIndex < names.size(); ++nameIndex) { std::string_view name; - if (const char* pcszName = mod.address_as(names[j]); pcszName < mod.address() || pcszName >= mod.address() + mod.image_size()) { + if (const char* pcszName = mod.address_as(names[nameIndex]); pcszName < mod.address() || pcszName >= mod.address() + mod.image_size()) { if (IsBadReadPtr(pcszName, 256)) { - logging::W("{} Name #{} points to an invalid address outside the executable. Skipping.", LogTag, j); + logging::W("{} Name #{} points to an invalid address outside the executable. Skipping.", LogTag, nameIndex); continue; } name = std::string_view(pcszName, strnlen(pcszName, 256)); - logging::W("{} Name #{} points to a seemingly valid address outside the executable: {}", LogTag, j, name); + logging::W("{} Name #{} points to a seemingly valid address outside the executable: {}", LogTag, nameIndex, name); } - if (ordinals[j] >= functions.size()) { - logging::W("{} Ordinal #{} points to function index #{} >= #{}. Skipping.", LogTag, j, ordinals[j], functions.size()); + if (ordinals[nameIndex] >= functions.size()) { + logging::W("{} Ordinal #{} points to function index #{} >= #{}. Skipping.", LogTag, nameIndex, ordinals[nameIndex], functions.size()); continue; } - const auto rva = functions[ordinals[j]]; - if (rva == §ion[i] - mod.address()) { + const auto rva = functions[ordinals[nameIndex]]; + if (rva == §ion[inst] - mod.address()) { resolvedExportName = std::format("[export:{}]", name); break; } @@ -135,7 +142,7 @@ void xivfixes::unhook_dll(bool bApply) { if (doRestore) { if (!tenderizer) tenderizer.emplace(section, PAGE_EXECUTE_READWRITE); - memcpy(§ion[i], &buf[i], instructionLength); + memcpy(§ion[inst], &buf[inst], instructionLength); } } @@ -147,21 +154,7 @@ void xivfixes::unhook_dll(bool bApply) { } catch (const std::exception& e) { logging::W("{} Error: {}", LogTag, e.what()); } - }; - - // This is needed since try and __try cannot be used in the same function. Lambdas circumvent the limitation. - const auto windows_exception_handler = [&]() { - for (size_t i = 0; i < mods.size(); i++) { - const auto& mod = mods[i]; - __try { - test_module(i, mod); - } __except (EXCEPTION_EXECUTE_HANDLER) { - logging::W("{} Error: Access Violation", LogTag); - } - } - }; - - windows_exception_handler(); + } } using TFnGetInputDeviceManager = void* (); @@ -294,13 +287,11 @@ static bool is_xivalex(const std::filesystem::path& dllPath) { static bool is_openprocess_already_dealt_with() { static const auto s_value = [] { for (const auto& mod : utils::loaded_module::all_modules()) { - try { - if (is_xivalex(mod.path())) - return true; - - } catch (...) { - // pass - } + const auto path = mod.path().value_or({}); + if (path.empty()) + continue; + if (is_xivalex(path)) + return true; } return false; }(); @@ -650,43 +641,22 @@ void xivfixes::symbol_load_patches(bool bApply) { void xivfixes::disable_game_debugging_protection(bool bApply) { static const char* LogTag = "[xivfixes:disable_game_debugging_protection]"; - static const std::vector patchBytes = { - 0x31, 0xC0, // XOR EAX, EAX - 0x90, // NOP - 0x90, // NOP - 0x90, // NOP - 0x90 // NOP - }; + static std::optional> s_hookIsDebuggerPresent; - if (!bApply) - return; + if (bApply) { + if (!g_startInfo.BootEnabledGameFixes.contains("disable_game_debugging_protection")) { + logging::I("{} Turned off via environment variable.", LogTag); + return; + } - if (!g_startInfo.BootEnabledGameFixes.contains("disable_game_debugging_protection")) { - logging::I("{} Turned off via environment variable.", LogTag); - return; - } - - // Find IsDebuggerPresent in Framework.Tick() - const char* matchPtr = utils::signature_finder() - .look_in(utils::loaded_module(g_hGameInstance), ".text") - .look_for_hex("FF 15 ?? ?? ?? ?? 85 C0 74 13 41") - .find_one() - .Match.data(); - - if (!matchPtr) { - logging::E("{} Failed to find signature.", LogTag); - return; - } - - void* address = const_cast(static_cast(matchPtr)); - - DWORD oldProtect; - if (VirtualProtect(address, patchBytes.size(), PAGE_EXECUTE_READWRITE, &oldProtect)) { - memcpy(address, patchBytes.data(), patchBytes.size()); - VirtualProtect(address, patchBytes.size(), oldProtect, &oldProtect); - logging::I("{} Patch applied at address 0x{:X}.", LogTag, reinterpret_cast(address)); + s_hookIsDebuggerPresent.emplace("kernel32.dll!IsDebuggerPresent", "kernel32.dll", "IsDebuggerPresent", 0); + s_hookIsDebuggerPresent->set_detour([]() { return false; }); + logging::I("{} Enable", LogTag); } else { - logging::E("{} Failed to change memory protection.", LogTag); + if (s_hookIsDebuggerPresent) { + logging::I("{} Disable", LogTag); + s_hookIsDebuggerPresent.reset(); + } } } diff --git a/Dalamud.Common/Dalamud.Common.csproj b/Dalamud.Common/Dalamud.Common.csproj index 54a182210..774b551ad 100644 --- a/Dalamud.Common/Dalamud.Common.csproj +++ b/Dalamud.Common/Dalamud.Common.csproj @@ -6,7 +6,7 @@ - + diff --git a/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj b/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj index bf718b28e..9b355a40b 100644 --- a/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj +++ b/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj @@ -25,10 +25,9 @@ - - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj b/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj index 1c89f4ff7..7f8de3843 100644 --- a/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj +++ b/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj @@ -38,7 +38,7 @@ Level3 true true - stdcpplatest + stdcpp23 pch.h ProgramDatabase CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) @@ -55,7 +55,7 @@ true false - MultiThreadedDebugDLL + MultiThreadedDebugDLL _DEBUG;%(PreprocessorDefinitions) @@ -67,7 +67,7 @@ true true - MultiThreadedDLL + MultiThreadedDLL NDEBUG;%(PreprocessorDefinitions) @@ -108,4 +108,4 @@ - + \ No newline at end of file diff --git a/Dalamud.Injector/Dalamud.Injector.csproj b/Dalamud.Injector/Dalamud.Injector.csproj index c8b648f6d..4a55174a1 100644 --- a/Dalamud.Injector/Dalamud.Injector.csproj +++ b/Dalamud.Injector/Dalamud.Injector.csproj @@ -52,17 +52,18 @@ - - - - - - - - - - - + + + + + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Dalamud.Injector/EntryPoint.cs b/Dalamud.Injector/EntryPoint.cs index 4f876102e..b876aa6ed 100644 --- a/Dalamud.Injector/EntryPoint.cs +++ b/Dalamud.Injector/EntryPoint.cs @@ -12,14 +12,13 @@ using System.Text.RegularExpressions; using Dalamud.Common; using Dalamud.Common.Game; using Dalamud.Common.Util; - using Newtonsoft.Json; using Reloaded.Memory.Buffers; using Serilog; using Serilog.Core; using Serilog.Events; - -using static Dalamud.Injector.NativeFunctions; +using Windows.Win32.Foundation; +using Windows.Win32.UI.WindowsAndMessaging; namespace Dalamud.Injector { @@ -268,9 +267,9 @@ namespace Dalamud.Injector private static OSPlatform DetectPlatformHeuristic() { - var ntdll = NativeFunctions.GetModuleHandleW("ntdll.dll"); - var wineServerCallPtr = NativeFunctions.GetProcAddress(ntdll, "wine_server_call"); - var wineGetHostVersionPtr = NativeFunctions.GetProcAddress(ntdll, "wine_get_host_version"); + var ntdll = Windows.Win32.PInvoke.GetModuleHandle("ntdll.dll"); + var wineServerCallPtr = Windows.Win32.PInvoke.GetProcAddress(ntdll, "wine_server_call"); + var wineGetHostVersionPtr = Windows.Win32.PInvoke.GetProcAddress(ntdll, "wine_get_host_version"); var winePlatform = GetWinePlatform(wineGetHostVersionPtr); var isWine = wineServerCallPtr != nint.Zero; @@ -621,10 +620,13 @@ namespace Dalamud.Injector if (warnManualInjection) { - var result = MessageBoxW(IntPtr.Zero, $"Take care: you are manually injecting Dalamud into FFXIV({string.Join(", ", processes.Select(x => $"{x.Id}"))}).\n\nIf you are doing this to use plugins before they are officially whitelisted on patch days, things may go wrong and you may get into trouble.\nWe discourage you from doing this and you won't be warned again in-game.", "Dalamud", MessageBoxType.IconWarning | MessageBoxType.OkCancel); + var result = Windows.Win32.PInvoke.MessageBox( + HWND.Null, + $"Take care: you are manually injecting Dalamud into FFXIV({string.Join(", ", processes.Select(x => $"{x.Id}"))}).\n\nIf you are doing this to use plugins before they are officially whitelisted on patch days, things may go wrong and you may get into trouble.\nWe discourage you from doing this and you won't be warned again in-game.", + "Dalamud", + MESSAGEBOX_STYLE.MB_ICONWARNING | MESSAGEBOX_STYLE.MB_OKCANCEL); - // IDCANCEL - if (result == 2) + if (result == MESSAGEBOX_RESULT.IDCANCEL) { Log.Information("User cancelled injection"); return -2; @@ -934,30 +936,48 @@ namespace Dalamud.Injector Inject(process, startInfo, false); } - var processHandleForOwner = IntPtr.Zero; + var processHandleForOwner = HANDLE.Null; if (handleOwner != IntPtr.Zero) { - if (!DuplicateHandle(Process.GetCurrentProcess().Handle, process.Handle, handleOwner, out processHandleForOwner, 0, false, DuplicateOptions.SameAccess)) + unsafe { - Log.Warning("Failed to call DuplicateHandle: Win32 error code {0}", Marshal.GetLastWin32Error()); + if (!Windows.Win32.PInvoke.DuplicateHandle( + new HANDLE(Process.GetCurrentProcess().Handle.ToPointer()), + new HANDLE(process.Handle.ToPointer()), + new HANDLE(handleOwner), + &processHandleForOwner, + 0, + false, + DUPLICATE_HANDLE_OPTIONS.DUPLICATE_SAME_ACCESS)) + { + Log.Warning("Failed to call DuplicateHandle: Win32 error code {0}", Marshal.GetLastWin32Error()); + } } } - Console.WriteLine($"{{\"pid\": {process.Id}, \"handle\": {processHandleForOwner}}}"); + Console.WriteLine($"{{\"pid\": {process.Id}, \"handle\": {(IntPtr)processHandleForOwner}}}"); Log.CloseAndFlush(); return 0; } - private static Process GetInheritableCurrentProcessHandle() + private static unsafe Process GetInheritableCurrentProcessHandle() { - if (!DuplicateHandle(Process.GetCurrentProcess().Handle, Process.GetCurrentProcess().Handle, Process.GetCurrentProcess().Handle, out var inheritableCurrentProcessHandle, 0, true, DuplicateOptions.SameAccess)) + var currentProcessHandle = new HANDLE(Process.GetCurrentProcess().Handle.ToPointer()); + var inheritableHandle = HANDLE.Null; + if (!Windows.Win32.PInvoke.DuplicateHandle( + currentProcessHandle, + currentProcessHandle, + currentProcessHandle, + &inheritableHandle, + 0, + true, + DUPLICATE_HANDLE_OPTIONS.DUPLICATE_SAME_ACCESS)) { - Log.Error("Failed to call DuplicateHandle: Win32 error code {0}", Marshal.GetLastWin32Error()); - return null; + throw new Win32Exception("Failed to call DuplicateHandle"); } - return new ExistingProcess(inheritableCurrentProcessHandle); + return new ExistingProcess(inheritableHandle); } private static int ProcessLaunchTestCommand(List args) @@ -1048,13 +1068,13 @@ namespace Dalamud.Injector } injector.GetFunctionAddress(bootModule, "Initialize", out var initAddress); - injector.CallRemoteFunction(initAddress, startInfoAddress, out var exitCode); + var exitCode = injector.CallRemoteFunction(initAddress, startInfoAddress); // ====================================================== if (exitCode > 0) { - Log.Error($"Dalamud.Boot::Initialize returned {exitCode}"); + Log.Error("Dalamud.Boot::Initialize returned {ExitCode}", exitCode); return; } diff --git a/Dalamud.Injector/Injector.cs b/Dalamud.Injector/Injector.cs index 17ca5ccb5..0b57e7cbd 100644 --- a/Dalamud.Injector/Injector.cs +++ b/Dalamud.Injector/Injector.cs @@ -13,8 +13,8 @@ using Reloaded.Memory.Buffers; using Reloaded.Memory.Sources; using Reloaded.Memory.Utilities; using Serilog; +using Windows.Win32.Foundation; -using static Dalamud.Injector.NativeFunctions; using static Iced.Intel.AssemblerRegisters; namespace Dalamud.Injector @@ -88,7 +88,7 @@ namespace Dalamud.Injector if (lpParameter == 0) throw new Exception("Unable to allocate LoadLibraryW parameter"); - this.CallRemoteFunction(this.loadLibraryShellPtr, lpParameter, out var err); + var err = this.CallRemoteFunction(this.loadLibraryShellPtr, lpParameter); this.extMemory.Read(this.loadLibraryRetPtr, out address); if (address == IntPtr.Zero) throw new Exception($"LoadLibraryW(\"{modulePath}\") failure: {new Win32Exception((int)err).Message} ({err})"); @@ -108,7 +108,7 @@ namespace Dalamud.Injector if (lpParameter == 0) throw new Exception("Unable to allocate GetProcAddress parameter ptr"); - this.CallRemoteFunction(this.getProcAddressShellPtr, lpParameter, out var err); + var err = this.CallRemoteFunction(this.getProcAddressShellPtr, lpParameter); this.extMemory.Read(this.getProcAddressRetPtr, out address); if (address == 0) throw new Exception($"GetProcAddress(0x{module:X}, \"{functionName}\") failure: {new Win32Exception((int)err).Message} ({err})"); @@ -119,27 +119,30 @@ namespace Dalamud.Injector /// /// Method address. /// Parameter address. - /// Thread exit code. - public void CallRemoteFunction(nuint methodAddress, nuint parameterAddress, out uint exitCode) + /// Thread exit code. + public unsafe uint CallRemoteFunction(nuint methodAddress, nuint parameterAddress) { // Create and initialize a thread at our address and parameter address. - var threadHandle = CreateRemoteThread( - this.targetProcess.Handle, - IntPtr.Zero, + var threadHandle = Windows.Win32.PInvoke.CreateRemoteThread( + new HANDLE(this.targetProcess.Handle.ToPointer()), + null, UIntPtr.Zero, - methodAddress, - parameterAddress, - CreateThreadFlags.RunImmediately, - out _); + (delegate* unmanaged[Stdcall])methodAddress, + parameterAddress.ToPointer(), + 0, // Run immediately + null); if (threadHandle == IntPtr.Zero) throw new Exception($"CreateRemoteThread failure: {Marshal.GetLastWin32Error()}"); - _ = WaitForSingleObject(threadHandle, uint.MaxValue); + _ = Windows.Win32.PInvoke.WaitForSingleObject(threadHandle, uint.MaxValue); - GetExitCodeThread(threadHandle, out exitCode); + uint exitCode = 0; + if (!Windows.Win32.PInvoke.GetExitCodeThread(threadHandle, &exitCode)) + throw new Exception($"GetExitCodeThread failure: {Marshal.GetLastWin32Error()}"); - CloseHandle(threadHandle); + Windows.Win32.PInvoke.CloseHandle(threadHandle); + return exitCode; } private void SetupLoadLibrary(ProcessModule kernel32Module, ExportFunction[] kernel32Exports) diff --git a/Dalamud.Injector/NativeFunctions.cs b/Dalamud.Injector/NativeFunctions.cs deleted file mode 100644 index 06add3acc..000000000 --- a/Dalamud.Injector/NativeFunctions.cs +++ /dev/null @@ -1,956 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -namespace Dalamud.Injector -{ - /// - /// Native user32 functions. - /// - internal static partial class NativeFunctions - { - /// - /// MB_* from winuser. - /// - public enum MessageBoxType : uint - { - /// - /// The default value for any of the various subtypes. - /// - DefaultValue = 0x0, - - // To indicate the buttons displayed in the message box, specify one of the following values. - - /// - /// The message box contains three push buttons: Abort, Retry, and Ignore. - /// - AbortRetryIgnore = 0x2, - - /// - /// The message box contains three push buttons: Cancel, Try Again, Continue. Use this message box type instead - /// of MB_ABORTRETRYIGNORE. - /// - CancelTryContinue = 0x6, - - /// - /// Adds a Help button to the message box. When the user clicks the Help button or presses F1, the system sends - /// a WM_HELP message to the owner. - /// - Help = 0x4000, - - /// - /// The message box contains one push button: OK. This is the default. - /// - Ok = DefaultValue, - - /// - /// The message box contains two push buttons: OK and Cancel. - /// - OkCancel = 0x1, - - /// - /// The message box contains two push buttons: Retry and Cancel. - /// - RetryCancel = 0x5, - - /// - /// The message box contains two push buttons: Yes and No. - /// - YesNo = 0x4, - - /// - /// The message box contains three push buttons: Yes, No, and Cancel. - /// - YesNoCancel = 0x3, - - // To display an icon in the message box, specify one of the following values. - - /// - /// An exclamation-point icon appears in the message box. - /// - IconExclamation = 0x30, - - /// - /// An exclamation-point icon appears in the message box. - /// - IconWarning = IconExclamation, - - /// - /// An icon consisting of a lowercase letter i in a circle appears in the message box. - /// - IconInformation = 0x40, - - /// - /// An icon consisting of a lowercase letter i in a circle appears in the message box. - /// - IconAsterisk = IconInformation, - - /// - /// A question-mark icon appears in the message box. - /// The question-mark message icon is no longer recommended because it does not clearly represent a specific type - /// of message and because the phrasing of a message as a question could apply to any message type. In addition, - /// users can confuse the message symbol question mark with Help information. Therefore, do not use this question - /// mark message symbol in your message boxes. The system continues to support its inclusion only for backward - /// compatibility. - /// - IconQuestion = 0x20, - - /// - /// A stop-sign icon appears in the message box. - /// - IconStop = 0x10, - - /// - /// A stop-sign icon appears in the message box. - /// - IconError = IconStop, - - /// - /// A stop-sign icon appears in the message box. - /// - IconHand = IconStop, - - // To indicate the default button, specify one of the following values. - - /// - /// The first button is the default button. - /// MB_DEFBUTTON1 is the default unless MB_DEFBUTTON2, MB_DEFBUTTON3, or MB_DEFBUTTON4 is specified. - /// - DefButton1 = DefaultValue, - - /// - /// The second button is the default button. - /// - DefButton2 = 0x100, - - /// - /// The third button is the default button. - /// - DefButton3 = 0x200, - - /// - /// The fourth button is the default button. - /// - DefButton4 = 0x300, - - // To indicate the modality of the dialog box, specify one of the following values. - - /// - /// The user must respond to the message box before continuing work in the window identified by the hWnd parameter. - /// However, the user can move to the windows of other threads and work in those windows. Depending on the hierarchy - /// of windows in the application, the user may be able to move to other windows within the thread. All child windows - /// of the parent of the message box are automatically disabled, but pop-up windows are not. MB_APPLMODAL is the - /// default if neither MB_SYSTEMMODAL nor MB_TASKMODAL is specified. - /// - ApplModal = DefaultValue, - - /// - /// Same as MB_APPLMODAL except that the message box has the WS_EX_TOPMOST style. - /// Use system-modal message boxes to notify the user of serious, potentially damaging errors that require immediate - /// attention (for example, running out of memory). This flag has no effect on the user's ability to interact with - /// windows other than those associated with hWnd. - /// - SystemModal = 0x1000, - - /// - /// Same as MB_APPLMODAL except that all the top-level windows belonging to the current thread are disabled if the - /// hWnd parameter is NULL. Use this flag when the calling application or library does not have a window handle - /// available but still needs to prevent input to other windows in the calling thread without suspending other threads. - /// - TaskModal = 0x2000, - - // To specify other options, use one or more of the following values. - - /// - /// Same as desktop of the interactive window station. For more information, see Window Stations. If the current - /// input desktop is not the default desktop, MessageBox does not return until the user switches to the default - /// desktop. - /// - DefaultDesktopOnly = 0x20000, - - /// - /// The text is right-justified. - /// - Right = 0x80000, - - /// - /// Displays message and caption text using right-to-left reading order on Hebrew and Arabic systems. - /// - RtlReading = 0x100000, - - /// - /// The message box becomes the foreground window. Internally, the system calls the SetForegroundWindow function - /// for the message box. - /// - SetForeground = 0x10000, - - /// - /// The message box is created with the WS_EX_TOPMOST window style. - /// - Topmost = 0x40000, - - /// - /// The caller is a service notifying the user of an event. The function displays a message box on the current active - /// desktop, even if there is no user logged on to the computer. - /// - ServiceNotification = 0x200000, - } - - /// - /// Displays a modal dialog box that contains a system icon, a set of buttons, and a brief application-specific message, - /// such as status or error information. The message box returns an integer value that indicates which button the user - /// clicked. - /// - /// - /// A handle to the owner window of the message box to be created. If this parameter is NULL, the message box has no - /// owner window. - /// - /// - /// The message to be displayed. If the string consists of more than one line, you can separate the lines using a carriage - /// return and/or linefeed character between each line. - /// - /// - /// The dialog box title. If this parameter is NULL, the default title is Error. - /// - /// The contents and behavior of the dialog box. This parameter can be a combination of flags from the following groups - /// of flags. - /// - /// - /// If a message box has a Cancel button, the function returns the IDCANCEL value if either the ESC key is pressed or - /// the Cancel button is selected. If the message box has no Cancel button, pressing ESC will no effect - unless an - /// MB_OK button is present. If an MB_OK button is displayed and the user presses ESC, the return value will be IDOK. - /// If the function fails, the return value is zero.To get extended error information, call GetLastError. If the function - /// succeeds, the return value is one of the ID* enum values. - /// - [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern int MessageBoxW(IntPtr hWnd, string text, string caption, MessageBoxType type); - } - - /// - /// Native kernel32 functions. - /// - internal static partial class NativeFunctions - { - /// - /// MEM_* from memoryapi. - /// - [Flags] - public enum AllocationType - { - /// - /// To coalesce two adjacent placeholders, specify MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS. When you coalesce - /// placeholders, lpAddress and dwSize must exactly match those of the placeholder. - /// - CoalescePlaceholders = 0x1, - - /// - /// Frees an allocation back to a placeholder (after you've replaced a placeholder with a private allocation using - /// VirtualAlloc2 or Virtual2AllocFromApp). To split a placeholder into two placeholders, specify - /// MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER. - /// - PreservePlaceholder = 0x2, - - /// - /// Allocates memory charges (from the overall size of memory and the paging files on disk) for the specified reserved - /// memory pages. The function also guarantees that when the caller later initially accesses the memory, the contents - /// will be zero. Actual physical pages are not allocated unless/until the virtual addresses are actually accessed. - /// To reserve and commit pages in one step, call VirtualAllocEx with MEM_COMMIT | MEM_RESERVE. Attempting to commit - /// a specific address range by specifying MEM_COMMIT without MEM_RESERVE and a non-NULL lpAddress fails unless the - /// entire range has already been reserved. The resulting error code is ERROR_INVALID_ADDRESS. An attempt to commit - /// a page that is already committed does not cause the function to fail. This means that you can commit pages without - /// first determining the current commitment state of each page. If lpAddress specifies an address within an enclave, - /// flAllocationType must be MEM_COMMIT. - /// - Commit = 0x1000, - - /// - /// Reserves a range of the process's virtual address space without allocating any actual physical storage in memory - /// or in the paging file on disk. You commit reserved pages by calling VirtualAllocEx again with MEM_COMMIT. To - /// reserve and commit pages in one step, call VirtualAllocEx with MEM_COMMIT | MEM_RESERVE. Other memory allocation - /// functions, such as malloc and LocalAlloc, cannot use reserved memory until it has been released. - /// - Reserve = 0x2000, - - /// - /// Decommits the specified region of committed pages. After the operation, the pages are in the reserved state. - /// The function does not fail if you attempt to decommit an uncommitted page. This means that you can decommit - /// a range of pages without first determining the current commitment state. The MEM_DECOMMIT value is not supported - /// when the lpAddress parameter provides the base address for an enclave. - /// - Decommit = 0x4000, - - /// - /// Releases the specified region of pages, or placeholder (for a placeholder, the address space is released and - /// available for other allocations). After this operation, the pages are in the free state. If you specify this - /// value, dwSize must be 0 (zero), and lpAddress must point to the base address returned by the VirtualAlloc function - /// when the region is reserved. The function fails if either of these conditions is not met. If any pages in the - /// region are committed currently, the function first decommits, and then releases them. The function does not - /// fail if you attempt to release pages that are in different states, some reserved and some committed. This means - /// that you can release a range of pages without first determining the current commitment state. - /// - Release = 0x8000, - - /// - /// Indicates that data in the memory range specified by lpAddress and dwSize is no longer of interest. The pages - /// should not be read from or written to the paging file. However, the memory block will be used again later, so - /// it should not be decommitted. This value cannot be used with any other value. Using this value does not guarantee - /// that the range operated on with MEM_RESET will contain zeros. If you want the range to contain zeros, decommit - /// the memory and then recommit it. When you use MEM_RESET, the VirtualAllocEx function ignores the value of fProtect. - /// However, you must still set fProtect to a valid protection value, such as PAGE_NOACCESS. VirtualAllocEx returns - /// an error if you use MEM_RESET and the range of memory is mapped to a file. A shared view is only acceptable - /// if it is mapped to a paging file. - /// - Reset = 0x80000, - - /// - /// MEM_RESET_UNDO should only be called on an address range to which MEM_RESET was successfully applied earlier. - /// It indicates that the data in the specified memory range specified by lpAddress and dwSize is of interest to - /// the caller and attempts to reverse the effects of MEM_RESET. If the function succeeds, that means all data in - /// the specified address range is intact. If the function fails, at least some of the data in the address range - /// has been replaced with zeroes. This value cannot be used with any other value. If MEM_RESET_UNDO is called on - /// an address range which was not MEM_RESET earlier, the behavior is undefined. When you specify MEM_RESET, the - /// VirtualAllocEx function ignores the value of flProtect. However, you must still set flProtect to a valid - /// protection value, such as PAGE_NOACCESS. - /// - ResetUndo = 0x1000000, - - /// - /// Reserves an address range that can be used to map Address Windowing Extensions (AWE) pages. This value must - /// be used with MEM_RESERVE and no other values. - /// - Physical = 0x400000, - - /// - /// Allocates memory at the highest possible address. This can be slower than regular allocations, especially when - /// there are many allocations. - /// - TopDown = 0x100000, - - /// - /// Causes the system to track pages that are written to in the allocated region. If you specify this value, you - /// must also specify MEM_RESERVE. To retrieve the addresses of the pages that have been written to since the region - /// was allocated or the write-tracking state was reset, call the GetWriteWatch function. To reset the write-tracking - /// state, call GetWriteWatch or ResetWriteWatch. The write-tracking feature remains enabled for the memory region - /// until the region is freed. - /// - WriteWatch = 0x200000, - - /// - /// Allocates memory using large page support. The size and alignment must be a multiple of the large-page minimum. - /// To obtain this value, use the GetLargePageMinimum function. If you specify this value, you must also specify - /// MEM_RESERVE and MEM_COMMIT. - /// - LargePages = 0x20000000, - } - - /// - /// Unprefixed flags from CreateRemoteThread. - /// - [Flags] - public enum CreateThreadFlags - { - /// - /// The thread runs immediately after creation. - /// - RunImmediately = 0x0, - - /// - /// The thread is created in a suspended state, and does not run until the ResumeThread function is called. - /// - CreateSuspended = 0x4, - - /// - /// The dwStackSize parameter specifies the initial reserve size of the stack. If this flag is not specified, dwStackSize specifies the commit size. - /// - StackSizeParamIsReservation = 0x10000, - } - - /// - /// DUPLICATE_* values for DuplicateHandle's dwDesiredAccess. - /// - [Flags] - public enum DuplicateOptions : uint - { - /// - /// Closes the source handle. This occurs regardless of any error status returned. - /// - CloseSource = 0x00000001, - - /// - /// Ignores the dwDesiredAccess parameter. The duplicate handle has the same access as the source handle. - /// - SameAccess = 0x00000002, - } - - /// - /// PAGE_* from memoryapi. - /// - [Flags] - public enum MemoryProtection - { - /// - /// Enables execute access to the committed region of pages. An attempt to write to the committed region results - /// in an access violation. This flag is not supported by the CreateFileMapping function. - /// - Execute = 0x10, - - /// - /// Enables execute or read-only access to the committed region of pages. An attempt to write to the committed region - /// results in an access violation. - /// - ExecuteRead = 0x20, - - /// - /// Enables execute, read-only, or read/write access to the committed region of pages. - /// - ExecuteReadWrite = 0x40, - - /// - /// Enables execute, read-only, or copy-on-write access to a mapped view of a file mapping object. An attempt to - /// write to a committed copy-on-write page results in a private copy of the page being made for the process. The - /// private page is marked as PAGE_EXECUTE_READWRITE, and the change is written to the new page. This flag is not - /// supported by the VirtualAlloc or VirtualAllocEx functions. - /// - ExecuteWriteCopy = 0x80, - - /// - /// Disables all access to the committed region of pages. An attempt to read from, write to, or execute the committed - /// region results in an access violation. This flag is not supported by the CreateFileMapping function. - /// - NoAccess = 0x01, - - /// - /// Enables read-only access to the committed region of pages. An attempt to write to the committed region results - /// in an access violation. If Data Execution Prevention is enabled, an attempt to execute code in the committed - /// region results in an access violation. - /// - ReadOnly = 0x02, - - /// - /// Enables read-only or read/write access to the committed region of pages. If Data Execution Prevention is enabled, - /// attempting to execute code in the committed region results in an access violation. - /// - ReadWrite = 0x04, - - /// - /// Enables read-only or copy-on-write access to a mapped view of a file mapping object. An attempt to write to - /// a committed copy-on-write page results in a private copy of the page being made for the process. The private - /// page is marked as PAGE_READWRITE, and the change is written to the new page. If Data Execution Prevention is - /// enabled, attempting to execute code in the committed region results in an access violation. This flag is not - /// supported by the VirtualAlloc or VirtualAllocEx functions. - /// - WriteCopy = 0x08, - - /// - /// Sets all locations in the pages as invalid targets for CFG. Used along with any execute page protection like - /// PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE and PAGE_EXECUTE_WRITECOPY. Any indirect call to locations - /// in those pages will fail CFG checks and the process will be terminated. The default behavior for executable - /// pages allocated is to be marked valid call targets for CFG. This flag is not supported by the VirtualProtect - /// or CreateFileMapping functions. - /// - TargetsInvalid = 0x40000000, - - /// - /// Pages in the region will not have their CFG information updated while the protection changes for VirtualProtect. - /// For example, if the pages in the region was allocated using PAGE_TARGETS_INVALID, then the invalid information - /// will be maintained while the page protection changes. This flag is only valid when the protection changes to - /// an executable type like PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE and PAGE_EXECUTE_WRITECOPY. - /// The default behavior for VirtualProtect protection change to executable is to mark all locations as valid call - /// targets for CFG. - /// - TargetsNoUpdate = TargetsInvalid, - - /// - /// Pages in the region become guard pages. Any attempt to access a guard page causes the system to raise a - /// STATUS_GUARD_PAGE_VIOLATION exception and turn off the guard page status. Guard pages thus act as a one-time - /// access alarm. For more information, see Creating Guard Pages. When an access attempt leads the system to turn - /// off guard page status, the underlying page protection takes over. If a guard page exception occurs during a - /// system service, the service typically returns a failure status indicator. This value cannot be used with - /// PAGE_NOACCESS. This flag is not supported by the CreateFileMapping function. - /// - Guard = 0x100, - - /// - /// Sets all pages to be non-cachable. Applications should not use this attribute except when explicitly required - /// for a device. Using the interlocked functions with memory that is mapped with SEC_NOCACHE can result in an - /// EXCEPTION_ILLEGAL_INSTRUCTION exception. The PAGE_NOCACHE flag cannot be used with the PAGE_GUARD, PAGE_NOACCESS, - /// or PAGE_WRITECOMBINE flags. The PAGE_NOCACHE flag can be used only when allocating private memory with the - /// VirtualAlloc, VirtualAllocEx, or VirtualAllocExNuma functions. To enable non-cached memory access for shared - /// memory, specify the SEC_NOCACHE flag when calling the CreateFileMapping function. - /// - NoCache = 0x200, - - /// - /// Sets all pages to be write-combined. Applications should not use this attribute except when explicitly required - /// for a device. Using the interlocked functions with memory that is mapped as write-combined can result in an - /// EXCEPTION_ILLEGAL_INSTRUCTION exception. The PAGE_WRITECOMBINE flag cannot be specified with the PAGE_NOACCESS, - /// PAGE_GUARD, and PAGE_NOCACHE flags. The PAGE_WRITECOMBINE flag can be used only when allocating private memory - /// with the VirtualAlloc, VirtualAllocEx, or VirtualAllocExNuma functions. To enable write-combined memory access - /// for shared memory, specify the SEC_WRITECOMBINE flag when calling the CreateFileMapping function. - /// - WriteCombine = 0x400, - } - - /// - /// PROCESS_* from processthreadsapi. - /// - [Flags] - public enum ProcessAccessFlags : uint - { - /// - /// All possible access rights for a process object. - /// - AllAccess = 0x001F0FFF, - - /// - /// Required to create a process. - /// - CreateProcess = 0x0080, - - /// - /// Required to create a thread. - /// - CreateThread = 0x0002, - - /// - /// Required to duplicate a handle using DuplicateHandle. - /// - DupHandle = 0x0040, - - /// - /// Required to retrieve certain information about a process, such as its token, exit code, - /// and priority class (see OpenProcessToken). - /// - QueryInformation = 0x0400, - - /// - /// Required to retrieve certain information about a process(see GetExitCodeProcess, GetPriorityClass, IsProcessInJob, - /// QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted - /// PROCESS_QUERY_LIMITED_INFORMATION. - /// - QueryLimitedInformation = 0x1000, - - /// - /// Required to set certain information about a process, such as its priority class (see SetPriorityClass). - /// - SetInformation = 0x0200, - - /// - /// Required to set memory limits using SetProcessWorkingSetSize. - /// - SetQuote = 0x0100, - - /// - /// Required to suspend or resume a process. - /// - SuspendResume = 0x0800, - - /// - /// Required to terminate a process using TerminateProcess. - /// - Terminate = 0x0001, - - /// - /// Required to perform an operation on the address space of a process(see VirtualProtectEx and WriteProcessMemory). - /// - VmOperation = 0x0008, - - /// - /// Required to read memory in a process using ReadProcessMemory. - /// - VmRead = 0x0010, - - /// - /// Required to write to memory in a process using WriteProcessMemory. - /// - VmWrite = 0x0020, - - /// - /// Required to wait for the process to terminate using the wait functions. - /// - Synchronize = 0x00100000, - } - - /// - /// WAIT_* from synchapi. - /// - public enum WaitResult - { - /// - /// The specified object is a mutex object that was not released by the thread that owned the mutex object - /// before the owning thread terminated.Ownership of the mutex object is granted to the calling thread and - /// the mutex state is set to nonsignaled. If the mutex was protecting persistent state information, you - /// should check it for consistency. - /// - Abandoned = 0x80, - - /// - /// The state of the specified object is signaled. - /// - Object0 = 0x0, - - /// - /// The time-out interval elapsed, and the object's state is nonsignaled. - /// - Timeout = 0x102, - - /// - /// The function has failed. To get extended error information, call GetLastError. - /// - WAIT_FAILED = 0xFFFFFFF, - } - - /// - /// Closes an open object handle. - /// - /// - /// A valid handle to an open object. - /// - /// - /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error - /// information, call GetLastError. If the application is running under a debugger, the function will throw an exception if it receives - /// either a handle value that is not valid or a pseudo-handle value. This can happen if you close a handle twice, or if you call - /// CloseHandle on a handle returned by the FindFirstFile function instead of calling the FindClose function. - /// - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool CloseHandle(IntPtr hObject); - - /// - /// Creates a thread that runs in the virtual address space of another process. Use the CreateRemoteThreadEx function - /// to create a thread that runs in the virtual address space of another process and optionally specify extended attributes. - /// - /// - /// A handle to the process in which the thread is to be created. The handle must have the PROCESS_CREATE_THREAD, - /// PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ access rights, and may fail without - /// these rights on certain platforms. For more information, see Process Security and Access Rights. - /// - /// - /// A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new thread and determines whether - /// child processes can inherit the returned handle. If lpThreadAttributes is NULL, the thread gets a default security descriptor - /// and the handle cannot be inherited. The access control lists (ACL) in the default security descriptor for a thread come from - /// the primary token of the creator. - /// - /// - /// The initial size of the stack, in bytes. The system rounds this value to the nearest page. If this parameter is 0 (zero), the - /// new thread uses the default size for the executable. For more information, see Thread Stack Size. - /// - /// - /// A pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread and represents the - /// starting address of the thread in the remote process. The function must exist in the remote process. For more information, - /// see ThreadProc. - /// - /// - /// A pointer to a variable to be passed to the thread function. - /// - /// - /// The flags that control the creation of the thread. - /// - /// - /// A pointer to a variable that receives the thread identifier. If this parameter is NULL, the thread identifier is not returned. - /// - /// - /// If the function succeeds, the return value is a handle to the new thread. If the function fails, the return value is - /// NULL.To get extended error information, call GetLastError. Note that CreateRemoteThread may succeed even if lpStartAddress - /// points to data, code, or is not accessible. If the start address is invalid when the thread runs, an exception occurs, and - /// the thread terminates. Thread termination due to a invalid start address is handled as an error exit for the thread's process. - /// This behavior is similar to the asynchronous nature of CreateProcess, where the process is created even if it refers to - /// invalid or missing dynamic-link libraries (DLL). - /// - [DllImport("kernel32.dll", SetLastError = true)] - public static extern IntPtr CreateRemoteThread( - IntPtr hProcess, - IntPtr lpThreadAttributes, - UIntPtr dwStackSize, - nuint lpStartAddress, - nuint lpParameter, - CreateThreadFlags dwCreationFlags, - out uint lpThreadId); - - /// - /// Retrieves the termination status of the specified thread. - /// - /// - /// A handle to the thread. The handle must have the THREAD_QUERY_INFORMATION or THREAD_QUERY_LIMITED_INFORMATION - /// access right.For more information, see Thread Security and Access Rights. - /// - /// - /// A pointer to a variable to receive the thread termination status. - /// - /// - /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get - /// extended error information, call GetLastError. - /// - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode); - - /// - /// Opens an existing local process object. - /// - /// - /// The access to the process object. This access right is checked against the security descriptor for the process. This parameter can be one or - /// more of the process access rights. If the caller has enabled the SeDebugPrivilege privilege, the requested access is granted regardless of the - /// contents of the security descriptor. - /// - /// - /// If this value is TRUE, processes created by this process will inherit the handle. Otherwise, the processes do not inherit this handle. - /// - /// - /// The identifier of the local process to be opened. If the specified process is the System Idle Process(0x00000000), the function fails and the - /// last error code is ERROR_INVALID_PARAMETER.If the specified process is the System process or one of the Client Server Run-Time Subsystem(CSRSS) - /// processes, this function fails and the last error code is ERROR_ACCESS_DENIED because their access restrictions prevent user-level code from - /// opening them. If you are using GetCurrentProcessId as an argument to this function, consider using GetCurrentProcess instead of OpenProcess, for - /// improved performance. - /// - /// - /// If the function succeeds, the return value is an open handle to the specified process. - /// If the function fails, the return value is NULL.To get extended error information, call GetLastError. - /// - [DllImport("kernel32.dll", SetLastError = true)] - public static extern IntPtr OpenProcess( - ProcessAccessFlags dwDesiredAccess, - bool bInheritHandle, - int dwProcessId); - - /// - /// See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex. - /// Reserves, commits, or changes the state of a region of memory within the virtual address space of a specified process. - /// The function initializes the memory it allocates to zero. To specify the NUMA node for the physical memory, see - /// VirtualAllocExNuma. - /// - /// - /// The handle to a process. The function allocates memory within the virtual address space of this process. The handle - /// must have the PROCESS_VM_OPERATION access right. For more information, see Process Security and Access Rights. - /// - /// - /// The pointer that specifies a desired starting address for the region of pages that you want to allocate. If you - /// are reserving memory, the function rounds this address down to the nearest multiple of the allocation granularity. - /// If you are committing memory that is already reserved, the function rounds this address down to the nearest page - /// boundary. To determine the size of a page and the allocation granularity on the host computer, use the GetSystemInfo - /// function. If lpAddress is NULL, the function determines where to allocate the region. If this address is within - /// an enclave that you have not initialized by calling InitializeEnclave, VirtualAllocEx allocates a page of zeros - /// for the enclave at that address. The page must be previously uncommitted, and will not be measured with the EEXTEND - /// instruction of the Intel Software Guard Extensions programming model. If the address in within an enclave that you - /// initialized, then the allocation operation fails with the ERROR_INVALID_ADDRESS error. - /// - /// - /// The size of the region of memory to allocate, in bytes. If lpAddress is NULL, the function rounds dwSize up to the - /// next page boundary. If lpAddress is not NULL, the function allocates all pages that contain one or more bytes in - /// the range from lpAddress to lpAddress+dwSize. This means, for example, that a 2-byte range that straddles a page - /// boundary causes the function to allocate both pages. - /// - /// - /// The type of memory allocation. This parameter must contain one of the MEM_* enum values. - /// - /// - /// The memory protection for the region of pages to be allocated. If the pages are being committed, you can specify - /// any one of the memory protection constants. - /// - /// - /// If the function succeeds, the return value is the base address of the allocated region of pages. If the function - /// fails, the return value is NULL.To get extended error information, call GetLastError. - /// - [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] - public static extern IntPtr VirtualAllocEx( - IntPtr hProcess, - IntPtr lpAddress, - int dwSize, - AllocationType flAllocationType, - MemoryProtection flProtect); - - /// - /// See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualfreeex. - /// Releases, decommits, or releases and decommits a region of memory within the virtual address space of a specified - /// process. - /// - /// - /// A handle to a process. The function frees memory within the virtual address space of the process. The handle must - /// have the PROCESS_VM_OPERATION access right.For more information, see Process Security and Access Rights. - /// - /// - /// A pointer to the starting address of the region of memory to be freed. If the dwFreeType parameter is MEM_RELEASE, - /// lpAddress must be the base address returned by the VirtualAllocEx function when the region is reserved. - /// - /// - /// The size of the region of memory to free, in bytes. If the dwFreeType parameter is MEM_RELEASE, dwSize must be 0 - /// (zero). The function frees the entire region that is reserved in the initial allocation call to VirtualAllocEx. - /// If dwFreeType is MEM_DECOMMIT, the function decommits all memory pages that contain one or more bytes in the range - /// from the lpAddress parameter to (lpAddress+dwSize). This means, for example, that a 2-byte region of memory that - /// straddles a page boundary causes both pages to be decommitted. If lpAddress is the base address returned by - /// VirtualAllocEx and dwSize is 0 (zero), the function decommits the entire region that is allocated by VirtualAllocEx. - /// After that, the entire region is in the reserved state. - /// - /// - /// The type of free operation. This parameter must be one of the MEM_* enum values. - /// - /// - /// If the function succeeds, the return value is a nonzero value. If the function fails, the return value is 0 (zero). - /// To get extended error information, call GetLastError. - /// - [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] - public static extern bool VirtualFreeEx( - IntPtr hProcess, - IntPtr lpAddress, - int dwSize, - AllocationType dwFreeType); - - /// - /// Waits until the specified object is in the signaled state or the time-out interval elapses. To enter an alertable wait - /// state, use the WaitForSingleObjectEx function.To wait for multiple objects, use WaitForMultipleObjects. - /// - /// - /// A handle to the object. For a list of the object types whose handles can be specified, see the following Remarks section. - /// If this handle is closed while the wait is still pending, the function's behavior is undefined. The handle must have the - /// SYNCHRONIZE access right. For more information, see Standard Access Rights. - /// - /// - /// The time-out interval, in milliseconds. If a nonzero value is specified, the function waits until the object is signaled - /// or the interval elapses. If dwMilliseconds is zero, the function does not enter a wait state if the object is not signaled; - /// it always returns immediately. If dwMilliseconds is INFINITE, the function will return only when the object is signaled. - /// - /// - /// If the function succeeds, the return value indicates the event that caused the function to return. - /// It can be one of the WaitResult values. - /// - [DllImport("kernel32.dll", SetLastError = true)] - public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); - - /// - /// Writes data to an area of memory in a specified process. The entire area to be written to must be accessible or - /// the operation fails. - /// - /// - /// A handle to the process memory to be modified. The handle must have PROCESS_VM_WRITE and PROCESS_VM_OPERATION access - /// to the process. - /// - /// - /// A pointer to the base address in the specified process to which data is written. Before data transfer occurs, the - /// system verifies that all data in the base address and memory of the specified size is accessible for write access, - /// and if it is not accessible, the function fails. - /// - /// - /// A pointer to the buffer that contains data to be written in the address space of the specified process. - /// - /// - /// The number of bytes to be written to the specified process. - /// - /// - /// A pointer to a variable that receives the number of bytes transferred into the specified process. This parameter - /// is optional. If lpNumberOfBytesWritten is NULL, the parameter is ignored. - /// - /// - /// If the function succeeds, the return value is nonzero. If the function fails, the return value is 0 (zero). To get - /// extended error information, call GetLastError.The function fails if the requested write operation crosses into an - /// area of the process that is inaccessible. - /// - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool WriteProcessMemory( - IntPtr hProcess, - IntPtr lpBaseAddress, - byte[] lpBuffer, - int dwSize, - out IntPtr lpNumberOfBytesWritten); - - /// - /// Duplicates an object handle. - /// - /// - /// A handle to the process with the handle to be duplicated. - /// - /// The handle must have the PROCESS_DUP_HANDLE access right. - /// - /// - /// The handle to be duplicated. This is an open object handle that is valid in the context of the source process. - /// For a list of objects whose handles can be duplicated, see the following Remarks section. - /// - /// - /// A handle to the process that is to receive the duplicated handle. - /// - /// The handle must have the PROCESS_DUP_HANDLE access right. - /// - /// - /// A pointer to a variable that receives the duplicate handle. This handle value is valid in the context of the target process. - /// - /// If hSourceHandle is a pseudo handle returned by GetCurrentProcess or GetCurrentThread, DuplicateHandle converts it to a real handle to a process or thread, respectively. - /// - /// If lpTargetHandle is NULL, the function duplicates the handle, but does not return the duplicate handle value to the caller. This behavior exists only for backward compatibility with previous versions of this function. You should not use this feature, as you will lose system resources until the target process terminates. - /// - /// This parameter is ignored if hTargetProcessHandle is NULL. - /// - /// - /// The access requested for the new handle. For the flags that can be specified for each object type, see the following Remarks section. - /// - /// This parameter is ignored if the dwOptions parameter specifies the DUPLICATE_SAME_ACCESS flag. Otherwise, the flags that can be specified depend on the type of object whose handle is to be duplicated. - /// - /// This parameter is ignored if hTargetProcessHandle is NULL. - /// - /// - /// A variable that indicates whether the handle is inheritable. If TRUE, the duplicate handle can be inherited by new processes created by the target process. If FALSE, the new handle cannot be inherited. - /// - /// This parameter is ignored if hTargetProcessHandle is NULL. - /// - /// - /// Optional actions. - /// - /// - /// If the function succeeds, the return value is nonzero. - /// - /// If the function fails, the return value is zero. To get extended error information, call GetLastError. - /// - /// - /// See https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-duplicatehandle. - /// - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool DuplicateHandle( - IntPtr hSourceProcessHandle, - IntPtr hSourceHandle, - IntPtr hTargetProcessHandle, - out IntPtr lpTargetHandle, - uint dwDesiredAccess, - [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, - DuplicateOptions dwOptions); - - /// - /// See https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlew. - /// Retrieves a module handle for the specified module. The module must have been loaded by the calling process. To - /// avoid the race conditions described in the Remarks section, use the GetModuleHandleEx function. - /// - /// - /// The name of the loaded module (either a .dll or .exe file). If the file name extension is omitted, the default - /// library extension .dll is appended. The file name string can include a trailing point character (.) to indicate - /// that the module name has no extension. The string does not have to specify a path. When specifying a path, be sure - /// to use backslashes (\), not forward slashes (/). The name is compared (case independently) to the names of modules - /// currently mapped into the address space of the calling process. If this parameter is NULL, GetModuleHandle returns - /// a handle to the file used to create the calling process (.exe file). The GetModuleHandle function does not retrieve - /// handles for modules that were loaded using the LOAD_LIBRARY_AS_DATAFILE flag.For more information, see LoadLibraryEx. - /// - /// - /// If the function succeeds, the return value is a handle to the specified module. If the function fails, the return - /// value is NULL.To get extended error information, call GetLastError. - /// - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr GetModuleHandleW(string lpModuleName); - - /// - /// Retrieves the address of an exported function or variable from the specified dynamic-link library (DLL). - /// - /// - /// A handle to the DLL module that contains the function or variable. The LoadLibrary, LoadLibraryEx, LoadPackagedLibrary, - /// or GetModuleHandle function returns this handle. The GetProcAddress function does not retrieve addresses from modules - /// that were loaded using the LOAD_LIBRARY_AS_DATAFILE flag.For more information, see LoadLibraryEx. - /// - /// - /// The function or variable name, or the function's ordinal value. If this parameter is an ordinal value, it must be - /// in the low-order word; the high-order word must be zero. - /// - /// - /// If the function succeeds, the return value is the address of the exported function or variable. If the function - /// fails, the return value is NULL.To get extended error information, call GetLastError. - /// - [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] - [SuppressMessage("Globalization", "CA2101:Specify marshaling for P/Invoke string arguments", Justification = "Ansi only")] - public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); - } -} diff --git a/Dalamud.Injector/NativeMethods.json b/Dalamud.Injector/NativeMethods.json new file mode 100644 index 000000000..ffb313dfc --- /dev/null +++ b/Dalamud.Injector/NativeMethods.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://aka.ms/CsWin32.schema.json", + "allowMarshaling": false +} diff --git a/Dalamud.Injector/NativeMethods.txt b/Dalamud.Injector/NativeMethods.txt new file mode 100644 index 000000000..ffcf192a2 --- /dev/null +++ b/Dalamud.Injector/NativeMethods.txt @@ -0,0 +1,8 @@ +CreateRemoteThread +WaitForSingleObject +GetExitCodeThread +DuplicateHandle + +MessageBox +GetModuleHandle +GetProcAddress diff --git a/Dalamud.Test/Dalamud.Test.csproj b/Dalamud.Test/Dalamud.Test.csproj index c6c8f8e8a..488534e5e 100644 --- a/Dalamud.Test/Dalamud.Test.csproj +++ b/Dalamud.Test/Dalamud.Test.csproj @@ -42,19 +42,19 @@ - - - - - - - - - + + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Dalamud.Test/Game/Text/SeStringHandling/SeStringTests.cs b/Dalamud.Test/Game/Text/SeStringHandling/SeStringTests.cs index 9a48a6615..2a19d6216 100644 --- a/Dalamud.Test/Game/Text/SeStringHandling/SeStringTests.cs +++ b/Dalamud.Test/Game/Text/SeStringHandling/SeStringTests.cs @@ -1,6 +1,10 @@ -using System; +using System; +using System.IO; + using Dalamud.Configuration; using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text.SeStringHandling.Payloads; + using Xunit; namespace Dalamud.Test.Game.Text.SeStringHandling @@ -50,19 +54,41 @@ namespace Dalamud.Test.Game.Text.SeStringHandling var config = new MockConfig { Text = seString }; PluginConfigurations.SerializeConfig(config); } - + [Fact] public void TestConfigDeserializable() { var builder = new SeStringBuilder(); var seString = builder.AddText("Some text").Build(); var config = new MockConfig { Text = seString }; - + // This relies on the type information being maintained, which is why we're using these // static methods instead of default serialization/deserialization. var configSerialized = PluginConfigurations.SerializeConfig(config); var configDeserialized = (MockConfig)PluginConfigurations.DeserializeConfig(configSerialized); Assert.Equal(config, configDeserialized); } + + [Theory] + [InlineData(49, 209)] + [InlineData(71, 7)] + [InlineData(62, 116)] + public void TestAutoTranslatePayloadReencode(uint group, uint key) + { + var payload = new AutoTranslatePayload(group, key); + + Assert.Equal(group, payload.Group); + Assert.Equal(key, payload.Key); + + var encoded = payload.Encode(); + using var stream = new MemoryStream(encoded); + using var reader = new BinaryReader(stream); + var decodedPayload = Payload.Decode(reader) as AutoTranslatePayload; + + Assert.Equal(group, decodedPayload.Group); + Assert.Equal(key, decodedPayload.Key); + + Assert.Equal(encoded, decodedPayload.Encode()); + } } } diff --git a/Dalamud.Test/Storage/ReliableFileStorageTests.cs b/Dalamud.Test/Storage/ReliableFileStorageTests.cs index ff56293e0..8a955e430 100644 --- a/Dalamud.Test/Storage/ReliableFileStorageTests.cs +++ b/Dalamud.Test/Storage/ReliableFileStorageTests.cs @@ -31,19 +31,19 @@ public class ReliableFileStorageTests .Select( i => Parallel.ForEachAsync( Enumerable.Range(1, 100), - (j, _) => + async (j, _) => { if (i % 2 == 0) { // ReSharper disable once AccessToDisposedClosure - rfs.Instance.WriteAllText(tempFile, j.ToString()); + await rfs.Instance.WriteAllTextAsync(tempFile, j.ToString()); } else if (i % 3 == 0) { try { // ReSharper disable once AccessToDisposedClosure - rfs.Instance.ReadAllText(tempFile); + await rfs.Instance.ReadAllTextAsync(tempFile); } catch (FileNotFoundException) { @@ -54,8 +54,6 @@ public class ReliableFileStorageTests { File.Delete(tempFile); } - - return ValueTask.CompletedTask; }))); } @@ -112,41 +110,41 @@ public class ReliableFileStorageTests } [Fact] - public void Exists_WhenFileInBackup_ReturnsTrue() + public async Task Exists_WhenFileInBackup_ReturnsTrue() { var tempFile = Path.Combine(CreateTempDir(), TestFileName); using var rfs = CreateRfs(); - rfs.Instance.WriteAllText(tempFile, TestFileContent1); + await rfs.Instance.WriteAllTextAsync(tempFile, TestFileContent1); File.Delete(tempFile); Assert.True(rfs.Instance.Exists(tempFile)); } [Fact] - public void Exists_WhenFileInBackup_WithDifferentContainerId_ReturnsFalse() + public async Task Exists_WhenFileInBackup_WithDifferentContainerId_ReturnsFalse() { var tempFile = Path.Combine(CreateTempDir(), TestFileName); using var rfs = CreateRfs(); - rfs.Instance.WriteAllText(tempFile, TestFileContent1); + await rfs.Instance.WriteAllTextAsync(tempFile, TestFileContent1); File.Delete(tempFile); Assert.False(rfs.Instance.Exists(tempFile, Guid.NewGuid())); } [Fact] - public void WriteAllText_ThrowsIfPathIsEmpty() + public async Task WriteAllText_ThrowsIfPathIsEmpty() { using var rfs = CreateRfs(); - Assert.Throws(() => rfs.Instance.WriteAllText("", TestFileContent1)); + await Assert.ThrowsAsync(async () => await rfs.Instance.WriteAllTextAsync("", TestFileContent1)); } [Fact] - public void WriteAllText_ThrowsIfPathIsNull() + public async Task WriteAllText_ThrowsIfPathIsNull() { using var rfs = CreateRfs(); - Assert.Throws(() => rfs.Instance.WriteAllText(null!, TestFileContent1)); + await Assert.ThrowsAsync(async () => await rfs.Instance.WriteAllTextAsync(null!, TestFileContent1)); } [Fact] @@ -155,26 +153,26 @@ public class ReliableFileStorageTests var tempFile = Path.Combine(CreateTempDir(), TestFileName); using var rfs = CreateRfs(); - rfs.Instance.WriteAllText(tempFile, TestFileContent1); + await rfs.Instance.WriteAllTextAsync(tempFile, TestFileContent1); Assert.True(File.Exists(tempFile)); - Assert.Equal(TestFileContent1, rfs.Instance.ReadAllText(tempFile, forceBackup: true)); + Assert.Equal(TestFileContent1, await rfs.Instance.ReadAllTextAsync(tempFile, forceBackup: true)); Assert.Equal(TestFileContent1, await File.ReadAllTextAsync(tempFile)); } [Fact] - public void WriteAllText_SeparatesContainers() + public async Task WriteAllText_SeparatesContainers() { var tempFile = Path.Combine(CreateTempDir(), TestFileName); var containerId = Guid.NewGuid(); using var rfs = CreateRfs(); - rfs.Instance.WriteAllText(tempFile, TestFileContent1); - rfs.Instance.WriteAllText(tempFile, TestFileContent2, containerId); + await rfs.Instance.WriteAllTextAsync(tempFile, TestFileContent1); + await rfs.Instance.WriteAllTextAsync(tempFile, TestFileContent2, containerId); File.Delete(tempFile); - Assert.Equal(TestFileContent1, rfs.Instance.ReadAllText(tempFile, forceBackup: true)); - Assert.Equal(TestFileContent2, rfs.Instance.ReadAllText(tempFile, forceBackup: true, containerId)); + Assert.Equal(TestFileContent1, await rfs.Instance.ReadAllTextAsync(tempFile, forceBackup: true)); + Assert.Equal(TestFileContent2, await rfs.Instance.ReadAllTextAsync(tempFile, forceBackup: true, containerId)); } [Fact] @@ -183,7 +181,7 @@ public class ReliableFileStorageTests var tempFile = Path.Combine(CreateTempDir(), TestFileName); using var rfs = CreateFailedRfs(); - rfs.Instance.WriteAllText(tempFile, TestFileContent1); + await rfs.Instance.WriteAllTextAsync(tempFile, TestFileContent1); Assert.True(File.Exists(tempFile)); Assert.Equal(TestFileContent1, await File.ReadAllTextAsync(tempFile)); @@ -195,38 +193,38 @@ public class ReliableFileStorageTests var tempFile = Path.Combine(CreateTempDir(), TestFileName); using var rfs = CreateRfs(); - rfs.Instance.WriteAllText(tempFile, TestFileContent1); - rfs.Instance.WriteAllText(tempFile, TestFileContent2); + await rfs.Instance.WriteAllTextAsync(tempFile, TestFileContent1); + await rfs.Instance.WriteAllTextAsync(tempFile, TestFileContent2); Assert.True(File.Exists(tempFile)); - Assert.Equal(TestFileContent2, rfs.Instance.ReadAllText(tempFile, forceBackup: true)); + Assert.Equal(TestFileContent2, await rfs.Instance.ReadAllTextAsync(tempFile, forceBackup: true)); Assert.Equal(TestFileContent2, await File.ReadAllTextAsync(tempFile)); } [Fact] - public void WriteAllText_SupportsNullContent() + public async Task WriteAllText_SupportsNullContent() { var tempFile = Path.Combine(CreateTempDir(), TestFileName); using var rfs = CreateRfs(); - rfs.Instance.WriteAllText(tempFile, null); + await rfs.Instance.WriteAllTextAsync(tempFile, null); Assert.True(File.Exists(tempFile)); - Assert.Equal("", rfs.Instance.ReadAllText(tempFile)); + Assert.Equal("", await rfs.Instance.ReadAllTextAsync(tempFile)); } [Fact] - public void ReadAllText_ThrowsIfPathIsEmpty() + public async Task ReadAllText_ThrowsIfPathIsEmpty() { using var rfs = CreateRfs(); - Assert.Throws(() => rfs.Instance.ReadAllText("")); + await Assert.ThrowsAsync(async () => await rfs.Instance.ReadAllTextAsync("")); } [Fact] - public void ReadAllText_ThrowsIfPathIsNull() + public async Task ReadAllText_ThrowsIfPathIsNull() { using var rfs = CreateRfs(); - Assert.Throws(() => rfs.Instance.ReadAllText(null!)); + await Assert.ThrowsAsync(async () => await rfs.Instance.ReadAllTextAsync(null!)); } [Fact] @@ -236,40 +234,40 @@ public class ReliableFileStorageTests await File.WriteAllTextAsync(tempFile, TestFileContent1); using var rfs = CreateRfs(); - Assert.Equal(TestFileContent1, rfs.Instance.ReadAllText(tempFile)); + Assert.Equal(TestFileContent1, await rfs.Instance.ReadAllTextAsync(tempFile)); } [Fact] - public void ReadAllText_WhenFileMissingWithBackup_ReturnsContent() + public async Task ReadAllText_WhenFileMissingWithBackup_ReturnsContent() { var tempFile = Path.Combine(CreateTempDir(), TestFileName); using var rfs = CreateRfs(); - rfs.Instance.WriteAllText(tempFile, TestFileContent1); + await rfs.Instance.WriteAllTextAsync(tempFile, TestFileContent1); File.Delete(tempFile); - Assert.Equal(TestFileContent1, rfs.Instance.ReadAllText(tempFile)); + Assert.Equal(TestFileContent1, await rfs.Instance.ReadAllTextAsync(tempFile)); } [Fact] - public void ReadAllText_WhenFileMissingWithBackup_ThrowsWithDifferentContainerId() + public async Task ReadAllText_WhenFileMissingWithBackup_ThrowsWithDifferentContainerId() { var tempFile = Path.Combine(CreateTempDir(), TestFileName); var containerId = Guid.NewGuid(); using var rfs = CreateRfs(); - rfs.Instance.WriteAllText(tempFile, TestFileContent1); + await rfs.Instance.WriteAllTextAsync(tempFile, TestFileContent1); File.Delete(tempFile); - Assert.Throws(() => rfs.Instance.ReadAllText(tempFile, containerId: containerId)); + await Assert.ThrowsAsync(async () => await rfs.Instance.ReadAllTextAsync(tempFile, containerId: containerId)); } [Fact] - public void ReadAllText_WhenFileMissing_ThrowsIfDbFailed() + public async Task ReadAllText_WhenFileMissing_ThrowsIfDbFailed() { var tempFile = Path.Combine(CreateTempDir(), TestFileName); using var rfs = CreateFailedRfs(); - Assert.Throws(() => rfs.Instance.ReadAllText(tempFile)); + await Assert.ThrowsAsync(async () => await rfs.Instance.ReadAllTextAsync(tempFile)); } [Fact] @@ -278,7 +276,7 @@ public class ReliableFileStorageTests var tempFile = Path.Combine(CreateTempDir(), TestFileName); await File.WriteAllTextAsync(tempFile, TestFileContent1); using var rfs = CreateRfs(); - rfs.Instance.ReadAllText(tempFile, text => Assert.Equal(TestFileContent1, text)); + await rfs.Instance.ReadAllTextAsync(tempFile, text => Assert.Equal(TestFileContent1, text)); } [Fact] @@ -290,7 +288,7 @@ public class ReliableFileStorageTests var readerCalledOnce = false; using var rfs = CreateRfs(); - Assert.Throws(() => rfs.Instance.ReadAllText(tempFile, Reader)); + await Assert.ThrowsAsync(async () => await rfs.Instance.ReadAllTextAsync(tempFile, Reader)); return; @@ -303,7 +301,7 @@ public class ReliableFileStorageTests } [Fact] - public void ReadAllText_WithReader_WhenReaderThrows_ReadsContentFromBackup() + public async Task ReadAllText_WithReader_WhenReaderThrows_ReadsContentFromBackup() { var tempFile = Path.Combine(CreateTempDir(), TestFileName); @@ -311,10 +309,10 @@ public class ReliableFileStorageTests var assertionCalled = false; using var rfs = CreateRfs(); - rfs.Instance.WriteAllText(tempFile, TestFileContent1); + await rfs.Instance.WriteAllTextAsync(tempFile, TestFileContent1); File.Delete(tempFile); - rfs.Instance.ReadAllText(tempFile, Reader); + await rfs.Instance.ReadAllTextAsync(tempFile, Reader); Assert.True(assertionCalled); return; @@ -335,17 +333,17 @@ public class ReliableFileStorageTests var tempFile = Path.Combine(CreateTempDir(), TestFileName); await File.WriteAllTextAsync(tempFile, TestFileContent1); using var rfs = CreateRfs(); - Assert.Throws(() => rfs.Instance.ReadAllText(tempFile, _ => throw new FileNotFoundException())); + await Assert.ThrowsAsync(async () => await rfs.Instance.ReadAllTextAsync(tempFile, _ => throw new FileNotFoundException())); } [Theory] [InlineData(true)] [InlineData(false)] - public void ReadAllText_WhenFileDoesNotExist_Throws(bool forceBackup) + public async Task ReadAllText_WhenFileDoesNotExist_Throws(bool forceBackup) { var tempFile = Path.Combine(CreateTempDir(), TestFileName); using var rfs = CreateRfs(); - Assert.Throws(() => rfs.Instance.ReadAllText(tempFile, forceBackup)); + await Assert.ThrowsAsync(async () => await rfs.Instance.ReadAllTextAsync(tempFile, forceBackup)); } private static DisposableReliableFileStorage CreateRfs() diff --git a/Dalamud.sln b/Dalamud.sln index eccce60c0..c3af00f44 100644 --- a/Dalamud.sln +++ b/Dalamud.sln @@ -11,6 +11,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution 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 EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "build", "build\build.csproj", "{94E5B016-02B1-459B-97D9-E783F28764B2}" @@ -74,6 +75,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StandaloneImGuiTestbed", "i EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImGuiScene", "imgui\ImGuiScene\ImGuiScene.csproj", "{66753AC7-0029-4373-9CC4-7760B1F46141}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lumina", "Lumina", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lumina.Excel.Generator", "lib\Lumina.Excel\src\Lumina.Excel.Generator\Lumina.Excel.Generator.csproj", "{5A44DF0C-C9DA-940F-4D6B-4A11D13AEA3D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lumina.Excel", "lib\Lumina.Excel\src\Lumina.Excel\Lumina.Excel.csproj", "{88FB719B-EB41-73C5-8D25-C03E0C69904F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -168,6 +175,14 @@ Global {66753AC7-0029-4373-9CC4-7760B1F46141}.Debug|Any CPU.Build.0 = Debug|x64 {66753AC7-0029-4373-9CC4-7760B1F46141}.Release|Any CPU.ActiveCfg = Release|x64 {66753AC7-0029-4373-9CC4-7760B1F46141}.Release|Any CPU.Build.0 = Release|x64 + {5A44DF0C-C9DA-940F-4D6B-4A11D13AEA3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A44DF0C-C9DA-940F-4D6B-4A11D13AEA3D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A44DF0C-C9DA-940F-4D6B-4A11D13AEA3D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A44DF0C-C9DA-940F-4D6B-4A11D13AEA3D}.Release|Any CPU.Build.0 = Release|Any CPU + {88FB719B-EB41-73C5-8D25-C03E0C69904F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -191,6 +206,9 @@ Global {9C70BD06-D52C-425E-9C14-5D66BC6046EF} = {A217B3DF-607A-4EFB-B107-3C4809348043} {4702A911-2513-478C-A434-2776393FDE77} = {A217B3DF-607A-4EFB-B107-3C4809348043} {66753AC7-0029-4373-9CC4-7760B1F46141} = {A217B3DF-607A-4EFB-B107-3C4809348043} + {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} 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 08bbeb938..d546dc517 100644 --- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs +++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs @@ -108,11 +108,6 @@ 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. /// @@ -250,7 +245,7 @@ internal sealed class DalamudConfiguration : IInternalDisposableService /// This setting is effected by the in-game "System Sounds" option and volume. /// [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "ABI")] - public bool EnablePluginUISoundEffects { get; set; } + public bool EnablePluginUISoundEffects { get; set; } = true; /// /// Gets or sets a value indicating whether an additional button allowing pinning and clickthrough options should be shown @@ -278,11 +273,6 @@ 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. @@ -497,19 +487,27 @@ 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 + /// /// Load a configuration from the provided path. /// /// Path to read from. /// File storage. /// The deserialized configuration file. - public static DalamudConfiguration Load(string path, ReliableFileStorage fs) + public static async Task Load(string path, ReliableFileStorage fs) { DalamudConfiguration deserialized = null; try { - fs.ReadAllText(path, text => + await fs.ReadAllTextAsync(path, text => { deserialized = JsonConvert.DeserializeObject(text, SerializerSettings); @@ -580,8 +578,6 @@ internal sealed class DalamudConfiguration : IInternalDisposableService { this.Save(); this.isSaveQueued = false; - - Log.Verbose("Config saved"); } } @@ -630,16 +626,20 @@ internal sealed class DalamudConfiguration : IInternalDisposableService // Wait for previous write to finish this.writeTask?.Wait(); - this.writeTask = Task.Run(() => + this.writeTask = Task.Run(async () => { - Service.Get().WriteAllText( - this.configPath, - JsonConvert.SerializeObject(this, SerializerSettings)); + await Service.Get().WriteAllTextAsync( + this.configPath, + JsonConvert.SerializeObject(this, SerializerSettings)); + Log.Verbose("DalamudConfiguration saved"); }).ContinueWith(t => { if (t.IsFaulted) { - Log.Error(t.Exception, "Failed to save DalamudConfiguration to {Path}", this.configPath); + Log.Error( + t.Exception, + "Failed to save DalamudConfiguration to {Path}", + this.configPath); } }); diff --git a/Dalamud/Configuration/PluginConfigurations.cs b/Dalamud/Configuration/PluginConfigurations.cs index 8e32fa992..fa2969d31 100644 --- a/Dalamud/Configuration/PluginConfigurations.cs +++ b/Dalamud/Configuration/PluginConfigurations.cs @@ -2,6 +2,8 @@ using System.IO; using System.Reflection; using Dalamud.Storage; +using Dalamud.Utility; + using Newtonsoft.Json; namespace Dalamud.Configuration; @@ -9,6 +11,7 @@ namespace Dalamud.Configuration; /// /// Configuration to store settings for a dalamud plugin. /// +[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; @@ -36,7 +39,7 @@ public sealed class PluginConfigurations public void Save(IPluginConfiguration config, string pluginName, Guid workingPluginId) { Service.Get() - .WriteAllText(this.GetConfigFile(pluginName).FullName, SerializeConfig(config), workingPluginId); + .WriteAllTextAsync(this.GetConfigFile(pluginName).FullName, SerializeConfig(config), workingPluginId).GetAwaiter().GetResult(); } /// @@ -52,12 +55,12 @@ public sealed class PluginConfigurations IPluginConfiguration? config = null; try { - Service.Get().ReadAllText(path.FullName, text => + Service.Get().ReadAllTextAsync(path.FullName, text => { config = DeserializeConfig(text); if (config == null) throw new Exception("Read config was null."); - }, workingPluginId); + }, workingPluginId).GetAwaiter().GetResult(); } catch (FileNotFoundException) { diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index 7c451d97f..d1f730d5e 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -6,7 +6,7 @@ XIV Launcher addon framework - 13.0.0.0 + 13.0.0.12 $(DalamudVersion) $(DalamudVersion) $(DalamudVersion) @@ -61,38 +61,38 @@ - - - - - - - - - - + + + + + + + + + all - - - - - - - - - - + + + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + + + + + + + imgui-frag.hlsl.bytes @@ -101,6 +101,7 @@ imgui-vertex.hlsl.bytes + @@ -109,6 +110,7 @@ + @@ -160,6 +162,9 @@ + + + @@ -167,6 +172,7 @@ $([System.Text.RegularExpressions.Regex]::Replace($(DalamudGitCommitCount), @"\t|\n|\r", "")) $([System.Text.RegularExpressions.Regex]::Replace($(DalamudGitCommitHash), @"\t|\n|\r", "")) + $([System.Text.RegularExpressions.Regex]::Replace($(DalamudGitBranch), @"\t|\n|\r", "")) $([System.Text.RegularExpressions.Regex]::Replace($(DalamudGitDescribeOutput), @"\t|\n|\r", "")) $([System.Text.RegularExpressions.Regex]::Replace($(ClientStructsGitDescribeOutput), @"\t|\n|\r", "")) @@ -180,6 +186,7 @@ Local build at $([System.DateTime]::Now.ToString(yyyy-MM-dd HH:mm:ss)) + ??? ??? @@ -203,6 +210,10 @@ <_Parameter1>GitCommitCount <_Parameter2>$(CommitCount) + + <_Parameter1>GitBranch + <_Parameter2>$(Branch) + <_Parameter1>GitHashClientStructs <_Parameter2>$(CommitHashClientStructs) diff --git a/Dalamud/Data/DataManager.cs b/Dalamud/Data/DataManager.cs index d017bf85a..ed0aa6c4d 100644 --- a/Dalamud/Data/DataManager.cs +++ b/Dalamud/Data/DataManager.cs @@ -41,7 +41,7 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager try { Log.Verbose("Starting data load..."); - + using (Timings.Start("Lumina Init")) { var luminaOptions = new LuminaOptions @@ -53,12 +53,25 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager DefaultExcelLanguage = this.Language.ToLumina(), }; - this.GameData = new( - Path.Combine(Path.GetDirectoryName(Environment.ProcessPath)!, "sqpack"), - luminaOptions) + try { - StreamPool = new(), - }; + 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; + } Log.Information("Lumina is ready: {0}", this.GameData.DataPath); @@ -71,7 +84,7 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager dalamud.StartInfo.TroubleshootingPackData); 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); } @@ -130,7 +143,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); /// @@ -138,7 +151,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); /// @@ -161,7 +174,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/EntryPoint.cs b/Dalamud/EntryPoint.cs index 9fc09a56b..15077f3d8 100644 --- a/Dalamud/EntryPoint.cs +++ b/Dalamud/EntryPoint.cs @@ -144,7 +144,8 @@ public sealed class EntryPoint // Load configuration first to get some early persistent state, like log level var fs = new ReliableFileStorage(Path.GetDirectoryName(info.ConfigurationPath)!); - var configuration = DalamudConfiguration.Load(info.ConfigurationPath!, fs); + var configuration = DalamudConfiguration.Load(info.ConfigurationPath!, fs) + .GetAwaiter().GetResult(); // Set the appropriate logging level from the configuration if (!configuration.LogSynchronously) diff --git a/Dalamud/Game/Addon/Events/AddonEventType.cs b/Dalamud/Game/Addon/Events/AddonEventType.cs index 2c88c797b..25beb13fc 100644 --- a/Dalamud/Game/Addon/Events/AddonEventType.cs +++ b/Dalamud/Game/Addon/Events/AddonEventType.cs @@ -74,7 +74,7 @@ public enum AddonEventType : byte /// /// Resize (ChatLogPanel). /// - Resize = 19, + Resize = 21, /// /// AtkComponentButton Press, sent on MouseDown on Button. diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs index 8a97f5cc2..c008db08f 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs @@ -38,7 +38,7 @@ public abstract unsafe class AddonArgs /// /// The name to check. /// Whether it is the case. - internal bool IsAddon(ReadOnlySpan name) + internal bool IsAddon(string name) { if (this.Addon.IsNull) return false; @@ -46,12 +46,10 @@ public abstract unsafe class AddonArgs if (name.Length is 0 or > 32) return false; - var addonName = this.Addon.Name; - - if (string.IsNullOrEmpty(addonName)) + if (string.IsNullOrEmpty(this.Addon.Name)) return false; - return name == addonName; + return name == this.Addon.Name; } /// diff --git a/Dalamud/Game/ChatHandlers.cs b/Dalamud/Game/ChatHandlers.cs index c57dd70b8..b1b798a8a 100644 --- a/Dalamud/Game/ChatHandlers.cs +++ b/Dalamud/Game/ChatHandlers.cs @@ -77,7 +77,7 @@ internal partial class ChatHandlers : IServiceType } // For injections while logged in - if (clientState.LocalPlayer != null && clientState.TerritoryType == 0 && !this.hasSeenLoadingMsg) + if (clientState.IsLoggedIn && clientState.TerritoryType == 0 && !this.hasSeenLoadingMsg) this.PrintWelcomeMessage(); #if !DEBUG && false @@ -124,11 +124,11 @@ internal partial class ChatHandlers : IServiceType var updateMessage = new SeStringBuilder() .AddText(Loc.Localize("DalamudUpdated", "Dalamud has been updated successfully!")) .AddUiForeground(500) - .AddText(" [") + .AddText(" [ ") .Add(linkPayload) - .AddText(Loc.Localize("DalamudClickToViewChangelogs", " Click here to view the changelog.")) + .AddText(Loc.Localize("DalamudClickToViewChangelogs", "Click here to view the changelog.")) .Add(RawPayload.LinkTerminator) - .AddText("]") + .AddText(" ]") .AddUiForegroundOff(); chatGui.Print(new XivChatEntry diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs index 244989476..89dd8b8b1 100644 --- a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs +++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs @@ -97,7 +97,7 @@ internal sealed class AetheryteEntry : IAetheryteEntry public uint GilCost => this.data.GilCost; /// - public bool IsFavourite => this.data.IsFavourite != 0; + public bool IsFavourite => this.data.IsFavourite; /// public bool IsSharedHouse => this.data.IsSharedHouse; diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs index a3d44d423..4a6d011e9 100644 --- a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs +++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Collections.Generic; +using Dalamud.Game.ClientState.Objects; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; @@ -22,7 +23,7 @@ namespace Dalamud.Game.ClientState.Aetherytes; internal sealed unsafe partial class AetheryteList : IServiceType, IAetheryteList { [ServiceManager.ServiceDependency] - private readonly ClientState clientState = Service.Get(); + private readonly ObjectTable objectTable = Service.Get(); private readonly Telepo* telepoInstance = Telepo.Instance(); @@ -37,7 +38,7 @@ internal sealed unsafe partial class AetheryteList : IServiceType, IAetheryteLis { get { - if (this.clientState.LocalPlayer == null) + if (this.objectTable.LocalPlayer == null) return 0; this.Update(); @@ -59,7 +60,7 @@ internal sealed unsafe partial class AetheryteList : IServiceType, IAetheryteLis return null; } - if (this.clientState.LocalPlayer == null) + if (this.objectTable.LocalPlayer == null) return null; return new AetheryteEntry(this.telepoInstance->TeleportList[index]); @@ -69,7 +70,7 @@ internal sealed unsafe partial class AetheryteList : IServiceType, IAetheryteLis private void Update() { // this is very very important as otherwise it crashes - if (this.clientState.LocalPlayer == null) + if (this.objectTable.LocalPlayer == null) return; this.telepoInstance->UpdateAetheryteList(); diff --git a/Dalamud/Game/ClientState/Buddy/BuddyList.cs b/Dalamud/Game/ClientState/Buddy/BuddyList.cs index 84cfd24a3..dbac76518 100644 --- a/Dalamud/Game/ClientState/Buddy/BuddyList.cs +++ b/Dalamud/Game/ClientState/Buddy/BuddyList.cs @@ -2,11 +2,13 @@ using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; +using Dalamud.Game.Player; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; -using FFXIVClientStructs.FFXIV.Client.Game.UI; +using CSBuddy = FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy; +using CSUIState = FFXIVClientStructs.FFXIV.Client.Game.UI.UIState; namespace Dalamud.Game.ClientState.Buddy; @@ -24,7 +26,7 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList private const uint InvalidObjectID = 0xE0000000; [ServiceManager.ServiceDependency] - private readonly ClientState clientState = Service.Get(); + private readonly PlayerState playerState = Service.Get(); [ServiceManager.ServiceConstructor] private BuddyList() @@ -69,7 +71,7 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList } } - private unsafe FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy* BuddyListStruct => &UIState.Instance()->Buddy; + private unsafe CSBuddy* BuddyListStruct => &CSUIState.Instance()->Buddy; /// public IBuddyMember? this[int index] @@ -105,10 +107,10 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList /// public IBuddyMember? CreateBuddyMemberReference(IntPtr address) { - if (this.clientState.LocalContentId == 0) + if (address == IntPtr.Zero) return null; - if (address == IntPtr.Zero) + if (!this.playerState.IsLoaded) return null; var buddy = new BuddyMember(address); diff --git a/Dalamud/Game/ClientState/Buddy/BuddyMember.cs b/Dalamud/Game/ClientState/Buddy/BuddyMember.cs index 025de611d..393598d32 100644 --- a/Dalamud/Game/ClientState/Buddy/BuddyMember.cs +++ b/Dalamud/Game/ClientState/Buddy/BuddyMember.cs @@ -19,8 +19,14 @@ public interface IBuddyMember /// /// Gets the object ID of this buddy. /// + [Obsolete("Renamed to EntityId")] uint ObjectId { get; } + /// + /// Gets the entity ID of this buddy. + /// + uint EntityId { get; } + /// /// Gets the actor associated with this buddy. /// @@ -83,6 +89,9 @@ internal unsafe class BuddyMember : IBuddyMember /// public uint ObjectId => this.Struct->EntityId; + /// + public uint EntityId => this.Struct->EntityId; + /// public IGameObject? GameObject => this.objectTable.SearchById(this.ObjectId); diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 4c32873c6..93720e1db 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -6,6 +6,7 @@ using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.Gui; using Dalamud.Game.Network.Internal; +using Dalamud.Game.Player; using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; @@ -15,14 +16,14 @@ using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Application.Network; using FFXIVClientStructs.FFXIV.Client.Game; -using FFXIVClientStructs.FFXIV.Client.Game.Event; -using FFXIVClientStructs.FFXIV.Client.Game.UI; +using FFXIVClientStructs.FFXIV.Client.Network; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI.Agent; using Lumina.Excel.Sheets; using Action = System.Action; +using CSUIState = FFXIVClientStructs.FFXIV.Client.Game.UI.UIState; namespace Dalamud.Game.ClientState; @@ -36,9 +37,9 @@ internal sealed class ClientState : IInternalDisposableService, IClientState private readonly GameLifecycle lifecycle; private readonly ClientStateAddressResolver address; - private readonly Hook setupTerritoryTypeHook; + private readonly Hook handleZoneInitPacketHook; private readonly Hook uiModuleHandlePacketHook; - private readonly Hook onLogoutHook; + private readonly Hook setCurrentInstanceHook; [ServiceManager.ServiceDependency] private readonly Framework framework = Service.Get(); @@ -46,6 +47,18 @@ internal sealed class ClientState : IInternalDisposableService, IClientState [ServiceManager.ServiceDependency] private readonly NetworkHandlers networkHandlers = Service.Get(); + [ServiceManager.ServiceDependency] + private readonly PlayerState playerState = Service.Get(); + + [ServiceManager.ServiceDependency] + private readonly ObjectTable objectTable = Service.Get(); + + private Hook onLogoutHook; + private bool initialized; + private ushort territoryTypeId; + private bool isPvP; + private uint mapId; + private uint instance; private bool lastConditionNone = true; [ServiceManager.ServiceConstructor] @@ -59,26 +72,37 @@ internal sealed class ClientState : IInternalDisposableService, IClientState this.ClientLanguage = (ClientLanguage)dalamud.StartInfo.Language; - var setTerritoryTypeAddr = EventFramework.Addresses.SetTerritoryTypeId.Value; - Log.Verbose($"SetupTerritoryType address {Util.DescribeAddress(setTerritoryTypeAddr)}"); - - this.setupTerritoryTypeHook = Hook.FromAddress(setTerritoryTypeAddr, this.SetupTerritoryTypeDetour); + this.handleZoneInitPacketHook = Hook.FromAddress(this.AddressResolver.HandleZoneInitPacket, this.HandleZoneInitPacketDetour); this.uiModuleHandlePacketHook = Hook.FromAddress((nint)UIModule.StaticVirtualTablePointer->HandlePacket, this.UIModuleHandlePacketDetour); - this.onLogoutHook = Hook.FromAddress((nint)LogoutCallbackInterface.StaticVirtualTablePointer->OnLogout, this.OnLogoutDetour); + this.setCurrentInstanceHook = Hook.FromAddress(this.AddressResolver.SetCurrentInstance, this.SetCurrentInstanceDetour); - this.framework.Update += this.FrameworkOnOnUpdateEvent; this.networkHandlers.CfPop += this.NetworkHandlersOnCfPop; - this.setupTerritoryTypeHook.Enable(); + this.handleZoneInitPacketHook.Enable(); this.uiModuleHandlePacketHook.Enable(); - this.onLogoutHook.Enable(); + this.setCurrentInstanceHook.Enable(); + + this.framework.RunOnTick(this.Setup); } private unsafe delegate void ProcessPacketPlayerSetupDelegate(nint a1, nint packet); + private unsafe delegate void HandleZoneInitPacketDelegate(nint a1, uint localPlayerEntityId, nint packet, byte type); + + private unsafe delegate void SetCurrentInstanceDelegate(NetworkModuleProxy* thisPtr, short instanceId); + + /// + public event Action ZoneInit; + /// public event Action? TerritoryChanged; + /// + public event Action? MapIdChanged; + + /// + public event Action? InstanceChanged; + /// public event IClientState.ClassJobChangeDelegate? ClassJobChanged; @@ -104,23 +128,73 @@ internal sealed class ClientState : IInternalDisposableService, IClientState public ClientLanguage ClientLanguage { get; } /// - public ushort TerritoryType { get; private set; } - - /// - public unsafe uint MapId + public ushort TerritoryType { - get + get => this.territoryTypeId; + private set { - var agentMap = AgentMap.Instance(); - return agentMap != null ? agentMap->CurrentMapId : 0; + if (this.territoryTypeId != value) + { + this.territoryTypeId = value; + + if (this.initialized) + { + Log.Debug("TerritoryType changed: {0}", value); + this.TerritoryChanged?.InvokeSafely(value); + } + + var rowRef = LuminaUtils.CreateRef(value); + if (rowRef.IsValid) + { + this.IsPvP = rowRef.Value.IsPvpZone; + } + } } } /// - public IPlayerCharacter? LocalPlayer => Service.GetNullable()?[0] as IPlayerCharacter; + public uint MapId + { + get => this.mapId; + private set + { + if (this.mapId != value) + { + this.mapId = value; + + if (this.initialized) + { + Log.Debug("MapId changed: {0}", value); + this.MapIdChanged?.InvokeSafely(value); + } + } + } + } /// - public unsafe ulong LocalContentId => PlayerState.Instance()->ContentId; + public uint Instance + { + get => this.instance; + private set + { + if (this.instance != value) + { + this.instance = value; + + if (this.initialized) + { + Log.Debug("Instance changed: {0}", value); + this.InstanceChanged?.InvokeSafely(value); + } + } + } + } + + /// + public IPlayerCharacter? LocalPlayer => this.objectTable.LocalPlayer; + + /// + public unsafe ulong LocalContentId => this.playerState.ContentId; /// public unsafe bool IsLoggedIn @@ -133,7 +207,31 @@ internal sealed class ClientState : IInternalDisposableService, IClientState } /// - public bool IsPvP { get; private set; } + public bool IsPvP + { + get => this.isPvP; + private set + { + if (this.isPvP != value) + { + this.isPvP = value; + + if (this.initialized) + { + if (value) + { + Log.Debug("EnterPvP"); + this.EnterPvP?.InvokeSafely(); + } + else + { + Log.Debug("LeavePvP"); + this.LeavePvP?.InvokeSafely(); + } + } + } + } + } /// public bool IsPvPExcludingDen => this.IsPvP && this.TerritoryType != 250; @@ -150,7 +248,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState public bool IsClientIdle(out ConditionFlag blockingFlag) { blockingFlag = 0; - if (this.LocalPlayer is null) return true; + if (this.objectTable.LocalPlayer is null) return true; var condition = Service.GetNullable(); @@ -158,7 +256,8 @@ internal sealed class ClientState : IInternalDisposableService, IClientState ConditionFlag.NormalConditions, ConditionFlag.Jumping, ConditionFlag.Mounted, - ConditionFlag.UsingFashionAccessory]); + ConditionFlag.UsingFashionAccessory, + ConditionFlag.OnFreeTrial]); blockingFlag = blockingConditions.FirstOrDefault(); return blockingFlag == 0; @@ -172,43 +271,44 @@ internal sealed class ClientState : IInternalDisposableService, IClientState /// void IInternalDisposableService.DisposeService() { - this.setupTerritoryTypeHook.Dispose(); + this.handleZoneInitPacketHook.Dispose(); this.uiModuleHandlePacketHook.Dispose(); this.onLogoutHook.Dispose(); + this.setCurrentInstanceHook.Dispose(); - this.framework.Update -= this.FrameworkOnOnUpdateEvent; + this.framework.Update -= this.OnFrameworkUpdate; this.networkHandlers.CfPop -= this.NetworkHandlersOnCfPop; } - private unsafe void SetupTerritoryTypeDetour(EventFramework* eventFramework, ushort territoryType) + private unsafe void Setup() { - Log.Debug("TerritoryType changed: {0}", territoryType); + this.onLogoutHook = Hook.FromAddress((nint)AgentLobby.Instance()->LogoutCallbackInterface.VirtualTable->OnLogout, this.OnLogoutDetour); + this.onLogoutHook.Enable(); - this.TerritoryType = territoryType; - this.TerritoryChanged?.InvokeSafely(territoryType); + this.TerritoryType = (ushort)GameMain.Instance()->CurrentTerritoryTypeId; + this.MapId = AgentMap.Instance()->CurrentMapId; + this.Instance = CSUIState.Instance()->PublicInstance.InstanceId; - var rowRef = LuminaUtils.CreateRef(territoryType); - if (rowRef.IsValid) + this.initialized = true; + + 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 isPvP = rowRef.Value.IsPvpZone; - if (isPvP != this.IsPvP) - { - this.IsPvP = isPvP; - - if (this.IsPvP) - { - Log.Debug("EnterPvP"); - this.EnterPvP?.InvokeSafely(); - } - else - { - Log.Debug("LeavePvP"); - this.LeavePvP?.InvokeSafely(); - } - } + 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"); } - - this.setupTerritoryTypeHook.Original(eventFramework, territoryType); } private unsafe void UIModuleHandlePacketDetour( @@ -259,8 +359,16 @@ internal sealed class ClientState : IInternalDisposableService, IClientState } } - private void FrameworkOnOnUpdateEvent(IFramework framework1) + private unsafe void SetCurrentInstanceDetour(NetworkModuleProxy* thisPtr, short instanceId) { + this.setCurrentInstanceHook.Original(thisPtr, instanceId); + this.Instance = (uint)instanceId; + } + + private unsafe void OnFrameworkUpdate(IFramework framework) + { + this.MapId = AgentMap.Instance()->CurrentMapId; + var condition = Service.GetNullable(); var gameGui = Service.GetNullable(); var data = Service.GetNullable(); @@ -268,7 +376,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState if (condition == null || gameGui == null || data == null) return; - if (condition.Any() && this.lastConditionNone && this.LocalPlayer != null) + if (condition.Any() && this.lastConditionNone && this.objectTable.LocalPlayer != null) { Log.Debug("Is login"); this.lastConditionNone = false; @@ -342,7 +450,10 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat /// internal ClientStatePluginScoped() { + this.clientStateService.ZoneInit += this.ZoneInitForward; this.clientStateService.TerritoryChanged += this.TerritoryChangedForward; + this.clientStateService.MapIdChanged += this.MapIdChangedForward; + this.clientStateService.InstanceChanged += this.InstanceChangedForward; this.clientStateService.ClassJobChanged += this.ClassJobChangedForward; this.clientStateService.LevelChanged += this.LevelChangedForward; this.clientStateService.Login += this.LoginForward; @@ -352,9 +463,18 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat this.clientStateService.CfPop += this.ContentFinderPopForward; } + /// + public event Action ZoneInit; + /// public event Action? TerritoryChanged; + /// + public event Action? MapIdChanged; + + /// + public event Action? InstanceChanged; + /// public event IClientState.ClassJobChangeDelegate? ClassJobChanged; @@ -385,6 +505,9 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat /// public uint MapId => this.clientStateService.MapId; + /// + public uint Instance => this.clientStateService.Instance; + /// public IPlayerCharacter? LocalPlayer => this.clientStateService.LocalPlayer; @@ -412,7 +535,10 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat /// void IInternalDisposableService.DisposeService() { + this.clientStateService.ZoneInit -= this.ZoneInitForward; this.clientStateService.TerritoryChanged -= this.TerritoryChangedForward; + this.clientStateService.MapIdChanged -= this.MapIdChangedForward; + this.clientStateService.InstanceChanged -= this.InstanceChangedForward; this.clientStateService.ClassJobChanged -= this.ClassJobChangedForward; this.clientStateService.LevelChanged -= this.LevelChangedForward; this.clientStateService.Login -= this.LoginForward; @@ -421,7 +547,12 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat this.clientStateService.LeavePvP -= this.ExitPvPForward; this.clientStateService.CfPop -= this.ContentFinderPopForward; + this.ZoneInit = null; this.TerritoryChanged = null; + this.MapIdChanged = null; + this.InstanceChanged = null; + this.ClassJobChanged = null; + this.LevelChanged = null; this.Login = null; this.Logout = null; this.EnterPvP = null; @@ -429,8 +560,14 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat this.CfPop = null; } + private void ZoneInitForward(ZoneInitEventArgs eventArgs) => this.ZoneInit?.Invoke(eventArgs); + private void TerritoryChangedForward(ushort territoryId) => this.TerritoryChanged?.Invoke(territoryId); + private void MapIdChangedForward(uint mapId) => this.MapIdChanged?.Invoke(mapId); + + private void InstanceChangedForward(uint instanceId) => this.InstanceChanged?.Invoke(instanceId); + private void ClassJobChangedForward(uint classJobId) => this.ClassJobChanged?.Invoke(classJobId); private void LevelChangedForward(uint classJobId, uint level) => this.LevelChanged?.Invoke(classJobId, level); diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index 97bc5dae1..2fc859d09 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -10,19 +10,24 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver /// /// Gets the address of the keyboard state. /// - public IntPtr KeyboardState { get; private set; } + public nint KeyboardState { get; private set; } /// /// Gets the address of the keyboard state index array which translates the VK enumeration to the key state. /// - public IntPtr KeyboardStateIndexArray { get; private set; } + public nint KeyboardStateIndexArray { get; private set; } // Functions /// - /// Gets the address of the method which sets up the player. + /// Gets the address of the method that handles the ZoneInit packet. /// - public IntPtr ProcessPacketPlayerSetup { get; private set; } + public nint HandleZoneInitPacket { get; private set; } + + /// + /// Gets the address of the method that sets the current public instance. + /// + public nint SetCurrentInstance { get; private set; } /// /// Scan for and setup any configured address pointers. @@ -30,7 +35,8 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver /// The signature scanner to facilitate setup. protected override void Setup64Bit(ISigScanner sig) { - this.ProcessPacketPlayerSetup = sig.ScanText("40 53 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 48 8B DA E8 ?? ?? ?? ?? 48 8B D3"); // not in cs struct + 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. // lea rcx, ds:1DB9F74h[rax*4] KeyboardState diff --git a/Dalamud/Game/ClientState/Conditions/ConditionFlag.cs b/Dalamud/Game/ClientState/Conditions/ConditionFlag.cs index aa6de6e7a..19451dd5c 100644 --- a/Dalamud/Game/ClientState/Conditions/ConditionFlag.cs +++ b/Dalamud/Game/ClientState/Conditions/ConditionFlag.cs @@ -65,7 +65,11 @@ public enum ConditionFlag /// RidingPillion = 10, - [Obsolete("Renamed to RidingPillion", true)] Mounted2 = 10, + /// + /// Unable to execute command while mounted. + /// + [Obsolete("Renamed to RidingPillion", true)] + Mounted2 = 10, /// /// Unable to execute command while in that position. @@ -429,7 +433,11 @@ public enum ConditionFlag /// MountImmobile = 88, - [Obsolete("Renamed to MountImmobile", true)] InThisState88 = 88, + /// + /// Unable to execute command in this state. + /// + [Obsolete("Renamed to MountImmobile", true)] + InThisState88 = 88, /// /// Unable to execute command in this state. diff --git a/Dalamud/Game/ClientState/Fates/Fate.cs b/Dalamud/Game/ClientState/Fates/Fate.cs index 504b690c3..f82109fd0 100644 --- a/Dalamud/Game/ClientState/Fates/Fate.cs +++ b/Dalamud/Game/ClientState/Fates/Fate.cs @@ -1,6 +1,7 @@ using System.Numerics; using Dalamud.Data; +using Dalamud.Game.Player; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Memory; @@ -150,15 +151,11 @@ internal unsafe partial class Fate /// True or false. public static bool IsValid(Fate fate) { - var clientState = Service.GetNullable(); - - if (fate == null || clientState == null) + if (fate == null) return false; - if (clientState.LocalContentId == 0) - return false; - - return true; + var playerState = Service.Get(); + return playerState.IsLoaded == true; } /// diff --git a/Dalamud/Game/ClientState/Fates/FateTable.cs b/Dalamud/Game/ClientState/Fates/FateTable.cs index 1bf557ad5..30b0f4102 100644 --- a/Dalamud/Game/ClientState/Fates/FateTable.cs +++ b/Dalamud/Game/ClientState/Fates/FateTable.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Collections.Generic; +using Dalamud.Game.Player; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; @@ -60,15 +61,11 @@ internal sealed partial class FateTable : IServiceType, IFateTable /// public bool IsValid(IFate fate) { - var clientState = Service.GetNullable(); - - if (fate == null || clientState == null) + if (fate == null) return false; - if (clientState.LocalContentId == 0) - return false; - - return true; + var playerState = Service.Get(); + return playerState.IsLoaded == true; } /// @@ -87,12 +84,11 @@ internal sealed partial class FateTable : IServiceType, IFateTable /// public IFate? CreateFateReference(IntPtr offset) { - var clientState = Service.Get(); - - if (clientState.LocalContentId == 0) + if (offset == IntPtr.Zero) return null; - if (offset == IntPtr.Zero) + var playerState = Service.Get(); + if (!playerState.IsLoaded) return null; return new Fate(offset); diff --git a/Dalamud/Game/ClientState/Objects/ObjectTable.cs b/Dalamud/Game/ClientState/Objects/ObjectTable.cs index 84c1b5693..b66dd4775 100644 --- a/Dalamud/Game/ClientState/Objects/ObjectTable.cs +++ b/Dalamud/Game/ClientState/Objects/ObjectTable.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.Player; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; @@ -31,16 +32,16 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable { private static int objectTableLength; - private readonly ClientState clientState; + [ServiceManager.ServiceDependency] + private readonly PlayerState playerState = Service.Get(); + private readonly CachedEntry[] cachedObjectTable; private readonly Enumerator?[] frameworkThreadEnumerators = new Enumerator?[4]; [ServiceManager.ServiceConstructor] - private unsafe ObjectTable(ClientState clientState) + private unsafe ObjectTable() { - this.clientState = clientState; - var nativeObjectTable = CSGameObjectManager.Instance()->Objects.IndexSorted; objectTableLength = nativeObjectTable.Length; @@ -66,6 +67,9 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable /// public int Length => objectTableLength; + /// + public IPlayerCharacter? LocalPlayer => this[0] as IPlayerCharacter; + /// public IEnumerable PlayerObjects => this.GetPlayerObjects(); @@ -142,10 +146,10 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable { ThreadSafety.AssertMainThread(); - if (this.clientState.LocalContentId == 0) + if (address == nint.Zero) return null; - if (address == nint.Zero) + if (!this.playerState.IsLoaded) return null; var obj = (CSGameObject*)address; diff --git a/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs b/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs index efd8b5b3b..238c81a72 100644 --- a/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs +++ b/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs @@ -77,10 +77,10 @@ internal unsafe class BattleChara : Character, IBattleChara public StatusList StatusList => new(this.Struct->GetStatusManager()); /// - public bool IsCasting => this.Struct->GetCastInfo()->IsCasting > 0; + public bool IsCasting => this.Struct->GetCastInfo()->IsCasting; /// - public bool IsCastInterruptible => this.Struct->GetCastInfo()->Interruptible > 0; + public bool IsCastInterruptible => this.Struct->GetCastInfo()->Interruptible; /// public byte CastActionType => (byte)this.Struct->GetCastInfo()->ActionType; diff --git a/Dalamud/Game/ClientState/Objects/Types/GameObject.cs b/Dalamud/Game/ClientState/Objects/Types/GameObject.cs index 4209100f0..4b331a479 100644 --- a/Dalamud/Game/ClientState/Objects/Types/GameObject.cs +++ b/Dalamud/Game/ClientState/Objects/Types/GameObject.cs @@ -1,9 +1,8 @@ using System.Numerics; -using System.Runtime.CompilerServices; using Dalamud.Game.ClientState.Objects.Enums; +using Dalamud.Game.Player; using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Memory; namespace Dalamud.Game.ClientState.Objects.Types; @@ -35,8 +34,14 @@ public interface IGameObject : IEquatable /// /// Gets the data ID for linking to other respective game data. /// + [Obsolete("Renamed to BaseId")] public uint DataId { get; } + /// + /// Gets the base ID for linking to other respective game data. + /// + public uint BaseId { get; } + /// /// Gets the ID of this GameObject's owner. /// @@ -164,15 +169,11 @@ internal partial class GameObject /// True or false. public static bool IsValid(IGameObject? actor) { - var clientState = Service.GetNullable(); - - if (actor is null || clientState == null) + if (actor == null) return false; - if (clientState.LocalContentId == 0) - return false; - - return true; + var playerState = Service.Get(); + return playerState.IsLoaded == true; } /// @@ -208,6 +209,9 @@ internal unsafe partial class GameObject : IGameObject /// public uint DataId => this.Struct->BaseId; + /// + public uint BaseId => this.Struct->BaseId; + /// public uint OwnerId => this.Struct->OwnerId; diff --git a/Dalamud/Game/ClientState/Party/PartyList.cs b/Dalamud/Game/ClientState/Party/PartyList.cs index a016a8211..9618b679c 100644 --- a/Dalamud/Game/ClientState/Party/PartyList.cs +++ b/Dalamud/Game/ClientState/Party/PartyList.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Dalamud.Game.Player; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; @@ -25,7 +26,7 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList private const int AllianceLength = 20; [ServiceManager.ServiceDependency] - private readonly ClientState clientState = Service.Get(); + private readonly PlayerState playerState = Service.Get(); [ServiceManager.ServiceConstructor] private PartyList() @@ -91,10 +92,7 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList /// public IPartyMember? CreatePartyMemberReference(IntPtr address) { - if (this.clientState.LocalContentId == 0) - return null; - - if (address == IntPtr.Zero) + if (address == IntPtr.Zero || !this.playerState.IsLoaded) return null; return new PartyMember(address); @@ -112,10 +110,7 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList /// public IPartyMember? CreateAllianceMemberReference(IntPtr address) { - if (this.clientState.LocalContentId == 0) - return null; - - if (address == IntPtr.Zero) + if (address == IntPtr.Zero || !this.playerState.IsLoaded) return null; return new PartyMember(address); diff --git a/Dalamud/Game/ClientState/Party/PartyMember.cs b/Dalamud/Game/ClientState/Party/PartyMember.cs index cf620a7ef..4c738d866 100644 --- a/Dalamud/Game/ClientState/Party/PartyMember.cs +++ b/Dalamud/Game/ClientState/Party/PartyMember.cs @@ -40,8 +40,14 @@ public interface IPartyMember /// /// Gets the actor ID of this party member. /// + [Obsolete("Renamed to EntityId")] uint ObjectId { get; } + /// + /// Gets the entity ID of this party member. + /// + uint EntityId { get; } + /// /// Gets the actor associated with this buddy. /// @@ -115,87 +121,55 @@ internal unsafe class PartyMember : IPartyMember this.Address = address; } - /// - /// Gets the address of this party member in memory. - /// + /// public IntPtr Address { get; } - /// - /// Gets a list of buffs or debuffs applied to this party member. - /// + /// public StatusList Statuses => new(&this.Struct->StatusManager); - /// - /// Gets the position of the party member. - /// - public Vector3 Position => new(this.Struct->X, this.Struct->Y, this.Struct->Z); + /// + public Vector3 Position => this.Struct->Position; - /// - /// Gets the content ID of the party member. - /// + /// public long ContentId => (long)this.Struct->ContentId; - /// - /// Gets the actor ID of this party member. - /// + /// public uint ObjectId => this.Struct->EntityId; - /// - /// Gets the actor associated with this buddy. - /// - /// - /// This iterates the actor table, it should be used with care. - /// - public IGameObject? GameObject => Service.Get().SearchById(this.ObjectId); + /// + public uint EntityId => this.Struct->EntityId; - /// - /// Gets the current HP of this party member. - /// + /// + public IGameObject? GameObject => Service.Get().SearchById(this.EntityId); + + /// public uint CurrentHP => this.Struct->CurrentHP; - /// - /// Gets the maximum HP of this party member. - /// + /// public uint MaxHP => this.Struct->MaxHP; - /// - /// Gets the current MP of this party member. - /// + /// public ushort CurrentMP => this.Struct->CurrentMP; - /// - /// Gets the maximum MP of this party member. - /// + /// public ushort MaxMP => this.Struct->MaxMP; - /// - /// Gets the territory this party member is located in. - /// + /// public RowRef Territory => LuminaUtils.CreateRef(this.Struct->TerritoryType); - /// - /// Gets the World this party member resides in. - /// + /// public RowRef World => LuminaUtils.CreateRef(this.Struct->HomeWorld); - /// - /// Gets the displayname of this party member. - /// + /// public SeString Name => SeString.Parse(this.Struct->Name); - /// - /// Gets the sex of this party member. - /// + /// public byte Sex => this.Struct->Sex; - /// - /// Gets the classjob of this party member. - /// + /// public RowRef ClassJob => LuminaUtils.CreateRef(this.Struct->ClassJob); - /// - /// Gets the level of this party member. - /// + /// public byte Level => this.Struct->Level; private FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember*)this.Address; diff --git a/Dalamud/Game/ClientState/Statuses/StatusList.cs b/Dalamud/Game/ClientState/Statuses/StatusList.cs index a38e45ea3..410ae9d7c 100644 --- a/Dalamud/Game/ClientState/Statuses/StatusList.cs +++ b/Dalamud/Game/ClientState/Statuses/StatusList.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Dalamud.Game.Player; + namespace Dalamud.Game.ClientState.Statuses; /// @@ -66,15 +68,14 @@ public sealed unsafe partial class StatusList /// The status object containing the requested data. public static StatusList? CreateStatusListReference(IntPtr address) { + if (address == IntPtr.Zero) + return null; + // 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 == IntPtr.Zero) + var playerState = Service.Get(); + if (!playerState.IsLoaded) return null; return new StatusList(address); @@ -87,12 +88,11 @@ public sealed unsafe partial class StatusList /// The status object containing the requested data. public static Status? CreateStatusReference(IntPtr address) { - var clientState = Service.Get(); - - if (clientState.LocalContentId == 0) + if (address == IntPtr.Zero) return null; - if (address == IntPtr.Zero) + var playerState = Service.Get(); + if (!playerState.IsLoaded) return null; return new Status(address); diff --git a/Dalamud/Game/ClientState/ZoneInit.cs b/Dalamud/Game/ClientState/ZoneInit.cs new file mode 100644 index 000000000..5c2213c90 --- /dev/null +++ b/Dalamud/Game/ClientState/ZoneInit.cs @@ -0,0 +1,90 @@ +using System.Linq; +using System.Text; + +using Dalamud.Data; + +using Lumina.Excel.Sheets; + +namespace Dalamud.Game.ClientState; + +/// +/// Provides event data for when the game should initialize a zone. +/// +public class ZoneInitEventArgs : EventArgs +{ + /// + /// Gets the territory type of the zone being entered. + /// + public TerritoryType TerritoryType { get; private set; } + + /// + /// Gets the instance number of the zone, used when multiple copies of an area are active. + /// + public ushort Instance { get; private set; } + + /// + /// Gets the associated content finder condition for the zone, if any. + /// + public ContentFinderCondition ContentFinderCondition { get; private set; } + + /// + /// Gets the current weather in the zone upon entry. + /// + public Weather Weather { get; private set; } + + /// + /// Gets the set of active festivals in the zone. + /// + public Festival[] ActiveFestivals { get; private set; } = []; + + /// + /// Gets the phases corresponding to the active festivals. + /// + public ushort[] ActiveFestivalPhases { get; private set; } = []; + + /// + /// Reads raw zone initialization data from a network packet and constructs the event arguments. + /// + /// A pointer to the raw packet data. + /// A populated from the packet. + public static unsafe ZoneInitEventArgs Read(nint packet) + { + var dataManager = Service.Get(); + var eventArgs = new ZoneInitEventArgs(); + + var flags = *(byte*)(packet + 0x12); + + eventArgs.TerritoryType = dataManager.GetExcelSheet().GetRow(*(ushort*)(packet + 0x02)); + eventArgs.Instance = flags >= 0 ? (ushort)0 : *(ushort*)(packet + 0x04); + eventArgs.ContentFinderCondition = dataManager.GetExcelSheet().GetRow(*(ushort*)(packet + 0x06)); + eventArgs.Weather = dataManager.GetExcelSheet().GetRow(*(byte*)(packet + 0x10)); + + const int NumFestivals = 4; + eventArgs.ActiveFestivals = new Festival[NumFestivals]; + eventArgs.ActiveFestivalPhases = new ushort[NumFestivals]; + + // There are also 4 festival ids and phases for PlayerState at +0x3E and +0x46 respectively, + // 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 + 0x2E + (i * 2))); + eventArgs.ActiveFestivalPhases[i] = *(ushort*)(packet + 0x36 + (i * 2)); + } + + return eventArgs; + } + + /// + public override string ToString() + { + var sb = new StringBuilder("ZoneInitEventArgs { "); + sb.Append($"TerritoryTypeId = {this.TerritoryType.RowId}, "); + sb.Append($"Instance = {this.Instance}, "); + sb.Append($"ContentFinderCondition = {this.ContentFinderCondition.RowId}, "); + sb.Append($"Weather = {this.Weather.RowId}, "); + sb.Append($"ActiveFestivals = [{string.Join(", ", this.ActiveFestivals.Select(f => f.RowId))}], "); + sb.Append($"ActiveFestivalPhases = [{string.Join(", ", this.ActiveFestivalPhases)}]"); + sb.Append(" }"); + return sb.ToString(); + } +} diff --git a/Dalamud/Game/Config/SystemConfigOption.cs b/Dalamud/Game/Config/SystemConfigOption.cs index 154992637..dcfe16751 100644 --- a/Dalamud/Game/Config/SystemConfigOption.cs +++ b/Dalamud/Game/Config/SystemConfigOption.cs @@ -1031,6 +1031,13 @@ public enum SystemConfigOption [GameConfigOption("TitleScreenType", ConfigType.UInt)] TitleScreenType, + /// + /// System option with the internal name DisplayObjectLimitType2. + /// This option is a UInt. + /// + [GameConfigOption("DisplayObjectLimitType2", ConfigType.UInt)] + DisplayObjectLimitType2, + /// /// System option with the internal name AccessibilitySoundVisualEnable. /// This option is a UInt. @@ -1115,6 +1122,13 @@ public enum SystemConfigOption [GameConfigOption("CameraZoom", ConfigType.UInt)] CameraZoom, + /// + /// System option with the internal name DynamicAroundRangeMode. + /// This option is a UInt. + /// + [GameConfigOption("DynamicAroundRangeMode", ConfigType.UInt)] + DynamicAroundRangeMode, + /// /// System option with the internal name DynamicRezoType. /// This option is a UInt. diff --git a/Dalamud/Game/Config/UiConfigOption.cs b/Dalamud/Game/Config/UiConfigOption.cs index 1a59b8945..f6a9aaa21 100644 --- a/Dalamud/Game/Config/UiConfigOption.cs +++ b/Dalamud/Game/Config/UiConfigOption.cs @@ -2032,6 +2032,13 @@ public enum UiConfigOption [GameConfigOption("NamePlateDispJobIconInInstanceOther", ConfigType.UInt)] NamePlateDispJobIconInInstanceOther, + /// + /// UiConfig option with the internal name LogNamePlateDispEnemyCast. + /// This option is a UInt. + /// + [GameConfigOption("LogNamePlateDispEnemyCast", ConfigType.UInt)] + LogNamePlateDispEnemyCast, + /// /// UiConfig option with the internal name ActiveInfo. /// This option is a UInt. @@ -2690,6 +2697,594 @@ public enum UiConfigOption [GameConfigOption("LogColorOtherClass", ConfigType.UInt)] LogColorOtherClass, + /// + /// UiConfig option with the internal name LogChatBubbleEnableChatBubble. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleEnableChatBubble", ConfigType.UInt)] + LogChatBubbleEnableChatBubble, + + /// + /// UiConfig option with the internal name LogChatBubbleShowMax. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleShowMax", ConfigType.UInt)] + LogChatBubbleShowMax, + + /// + /// UiConfig option with the internal name LogChatBubbleShowOwnMessage. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleShowOwnMessage", ConfigType.UInt)] + LogChatBubbleShowOwnMessage, + + /// + /// UiConfig option with the internal name LogChatBubbleShowDuringBattle. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleShowDuringBattle", ConfigType.UInt)] + LogChatBubbleShowDuringBattle, + + /// + /// UiConfig option with the internal name LogChatBubbleSizeType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleSizeType", ConfigType.UInt)] + LogChatBubbleSizeType, + + /// + /// UiConfig option with the internal name LogChatBubbleShowLargePvP. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleShowLargePvP", ConfigType.UInt)] + LogChatBubbleShowLargePvP, + + /// + /// UiConfig option with the internal name LogChatBubbleShowQuickChat. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleShowQuickChat", ConfigType.UInt)] + LogChatBubbleShowQuickChat, + + /// + /// UiConfig option with the internal name LogChatBubbleDispRowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleDispRowType", ConfigType.UInt)] + LogChatBubbleDispRowType, + + /// + /// UiConfig option with the internal name LogChatBubbleSayShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleSayShowType", ConfigType.UInt)] + LogChatBubbleSayShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleSayFontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleSayFontColor", ConfigType.UInt)] + LogChatBubbleSayFontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleSayWindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleSayWindowColor", ConfigType.UInt)] + LogChatBubbleSayWindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleYellShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleYellShowType", ConfigType.UInt)] + LogChatBubbleYellShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleYellFontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleYellFontColor", ConfigType.UInt)] + LogChatBubbleYellFontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleYellWindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleYellWindowColor", ConfigType.UInt)] + LogChatBubbleYellWindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleShoutShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleShoutShowType", ConfigType.UInt)] + LogChatBubbleShoutShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleShoutFontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleShoutFontColor", ConfigType.UInt)] + LogChatBubbleShoutFontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleShoutWindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleShoutWindowColor", ConfigType.UInt)] + LogChatBubbleShoutWindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleTellShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleTellShowType", ConfigType.UInt)] + LogChatBubbleTellShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleTellFontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleTellFontColor", ConfigType.UInt)] + LogChatBubbleTellFontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleTellWindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleTellWindowColor", ConfigType.UInt)] + LogChatBubbleTellWindowColor, + + /// + /// UiConfig option with the internal name LogChatBubblePartyShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubblePartyShowType", ConfigType.UInt)] + LogChatBubblePartyShowType, + + /// + /// UiConfig option with the internal name LogChatBubblePartyFontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubblePartyFontColor", ConfigType.UInt)] + LogChatBubblePartyFontColor, + + /// + /// UiConfig option with the internal name LogChatBubblePartyWindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubblePartyWindowColor", ConfigType.UInt)] + LogChatBubblePartyWindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleAllianceShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleAllianceShowType", ConfigType.UInt)] + LogChatBubbleAllianceShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleAllianceFontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleAllianceFontColor", ConfigType.UInt)] + LogChatBubbleAllianceFontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleAllianceWindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleAllianceWindowColor", ConfigType.UInt)] + LogChatBubbleAllianceWindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleFcShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleFcShowType", ConfigType.UInt)] + LogChatBubbleFcShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleFcFontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleFcFontColor", ConfigType.UInt)] + LogChatBubbleFcFontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleFcWindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleFcWindowColor", ConfigType.UInt)] + LogChatBubbleFcWindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleBeginnerShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleBeginnerShowType", ConfigType.UInt)] + LogChatBubbleBeginnerShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleBeginnerFontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleBeginnerFontColor", ConfigType.UInt)] + LogChatBubbleBeginnerFontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleBeginnerWindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleBeginnerWindowColor", ConfigType.UInt)] + LogChatBubbleBeginnerWindowColor, + + /// + /// UiConfig option with the internal name LogChatBubblePvpteamShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubblePvpteamShowType", ConfigType.UInt)] + LogChatBubblePvpteamShowType, + + /// + /// UiConfig option with the internal name LogChatBubblePvpteamFontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubblePvpteamFontColor", ConfigType.UInt)] + LogChatBubblePvpteamFontColor, + + /// + /// UiConfig option with the internal name LogChatBubblePvpteamWindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubblePvpteamWindowColor", ConfigType.UInt)] + LogChatBubblePvpteamWindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs1ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs1ShowType", ConfigType.UInt)] + LogChatBubbleLs1ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleLs1FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs1FontColor", ConfigType.UInt)] + LogChatBubbleLs1FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs1WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs1WindowColor", ConfigType.UInt)] + LogChatBubbleLs1WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs2ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs2ShowType", ConfigType.UInt)] + LogChatBubbleLs2ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleLs2FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs2FontColor", ConfigType.UInt)] + LogChatBubbleLs2FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs2WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs2WindowColor", ConfigType.UInt)] + LogChatBubbleLs2WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs3ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs3ShowType", ConfigType.UInt)] + LogChatBubbleLs3ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleLs3FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs3FontColor", ConfigType.UInt)] + LogChatBubbleLs3FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs3WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs3WindowColor", ConfigType.UInt)] + LogChatBubbleLs3WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs4ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs4ShowType", ConfigType.UInt)] + LogChatBubbleLs4ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleLs4FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs4FontColor", ConfigType.UInt)] + LogChatBubbleLs4FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs4WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs4WindowColor", ConfigType.UInt)] + LogChatBubbleLs4WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs5ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs5ShowType", ConfigType.UInt)] + LogChatBubbleLs5ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleLs5FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs5FontColor", ConfigType.UInt)] + LogChatBubbleLs5FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs5WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs5WindowColor", ConfigType.UInt)] + LogChatBubbleLs5WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs6ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs6ShowType", ConfigType.UInt)] + LogChatBubbleLs6ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleLs6FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs6FontColor", ConfigType.UInt)] + LogChatBubbleLs6FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs6WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs6WindowColor", ConfigType.UInt)] + LogChatBubbleLs6WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs7ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs7ShowType", ConfigType.UInt)] + LogChatBubbleLs7ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleLs7FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs7FontColor", ConfigType.UInt)] + LogChatBubbleLs7FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs7WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs7WindowColor", ConfigType.UInt)] + LogChatBubbleLs7WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs8ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs8ShowType", ConfigType.UInt)] + LogChatBubbleLs8ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleLs8FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs8FontColor", ConfigType.UInt)] + LogChatBubbleLs8FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleLs8WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleLs8WindowColor", ConfigType.UInt)] + LogChatBubbleLs8WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls1ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls1ShowType", ConfigType.UInt)] + LogChatBubbleCwls1ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls1FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls1FontColor", ConfigType.UInt)] + LogChatBubbleCwls1FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls1WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls1WindowColor", ConfigType.UInt)] + LogChatBubbleCwls1WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls2ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls2ShowType", ConfigType.UInt)] + LogChatBubbleCwls2ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls2FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls2FontColor", ConfigType.UInt)] + LogChatBubbleCwls2FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls2WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls2WindowColor", ConfigType.UInt)] + LogChatBubbleCwls2WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls3ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls3ShowType", ConfigType.UInt)] + LogChatBubbleCwls3ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls3FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls3FontColor", ConfigType.UInt)] + LogChatBubbleCwls3FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls3WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls3WindowColor", ConfigType.UInt)] + LogChatBubbleCwls3WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls4ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls4ShowType", ConfigType.UInt)] + LogChatBubbleCwls4ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls4FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls4FontColor", ConfigType.UInt)] + LogChatBubbleCwls4FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls4WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls4WindowColor", ConfigType.UInt)] + LogChatBubbleCwls4WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls5ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls5ShowType", ConfigType.UInt)] + LogChatBubbleCwls5ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls5FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls5FontColor", ConfigType.UInt)] + LogChatBubbleCwls5FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls5WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls5WindowColor", ConfigType.UInt)] + LogChatBubbleCwls5WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls6ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls6ShowType", ConfigType.UInt)] + LogChatBubbleCwls6ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls6FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls6FontColor", ConfigType.UInt)] + LogChatBubbleCwls6FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls6WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls6WindowColor", ConfigType.UInt)] + LogChatBubbleCwls6WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls7ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls7ShowType", ConfigType.UInt)] + LogChatBubbleCwls7ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls7FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls7FontColor", ConfigType.UInt)] + LogChatBubbleCwls7FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls7WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls7WindowColor", ConfigType.UInt)] + LogChatBubbleCwls7WindowColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls8ShowType. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls8ShowType", ConfigType.UInt)] + LogChatBubbleCwls8ShowType, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls8FontColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls8FontColor", ConfigType.UInt)] + LogChatBubbleCwls8FontColor, + + /// + /// UiConfig option with the internal name LogChatBubbleCwls8WindowColor. + /// This option is a UInt. + /// + [GameConfigOption("LogChatBubbleCwls8WindowColor", ConfigType.UInt)] + LogChatBubbleCwls8WindowColor, + + /// + /// UiConfig option with the internal name LogPermeationRateInput. + /// This option is a UInt. + /// + [GameConfigOption("LogPermeationRateInput", ConfigType.UInt)] + LogPermeationRateInput, + /// /// UiConfig option with the internal name ChatType. /// This option is a UInt. @@ -3453,6 +4048,27 @@ public enum UiConfigOption [GameConfigOption("GPoseMotionFilterAction", ConfigType.UInt)] GPoseMotionFilterAction, + /// + /// UiConfig option with the internal name GPoseRollRotationCameraCorrection. + /// This option is a UInt. + /// + [GameConfigOption("GPoseRollRotationCameraCorrection", ConfigType.UInt)] + GPoseRollRotationCameraCorrection, + + /// + /// UiConfig option with the internal name GPoseInitCameraFocus. + /// This option is a UInt. + /// + [GameConfigOption("GPoseInitCameraFocus", ConfigType.UInt)] + GPoseInitCameraFocus, + + /// + /// UiConfig option with the internal name GposePortraitRotateType. + /// This option is a UInt. + /// + [GameConfigOption("GposePortraitRotateType", ConfigType.UInt)] + GposePortraitRotateType, + /// /// UiConfig option with the internal name LsListSortPriority. /// This option is a UInt. diff --git a/Dalamud/Game/Gui/AgentUpdateFlag.cs b/Dalamud/Game/Gui/AgentUpdateFlag.cs new file mode 100644 index 000000000..3e2808adb --- /dev/null +++ b/Dalamud/Game/Gui/AgentUpdateFlag.cs @@ -0,0 +1,34 @@ +using FFXIVClientStructs.FFXIV.Client.UI.Agent; + +namespace Dalamud.Game.Gui; + +/// +/// Represents a flag set by the game used by agents to conditionally update their addons. +/// +[Flags] +public enum AgentUpdateFlag : byte +{ + /// Set when an inventory has been updated. + InventoryUpdate = 1 << 0, + + /// Set when a hotbar slot has been executed, or a Gearset or Macro has been changed. + ActionBarUpdate = 1 << 1, + + /// Set when the RetainerMarket inventory has been updated. + RetainerMarketInventoryUpdate = 1 << 2, + + // /// Unknown use case. + // NameplateUpdate = 1 << 3, + + /// Set when the player unlocked collectibles, contents or systems. + UnlocksUpdate = 1 << 4, + + /// Set when was called. + MainCommandEnabledStateUpdate = 1 << 5, + + /// Set when any housing inventory has been updated. + HousingInventoryUpdate = 1 << 6, + + /// Set when any content inventory has been updated. + ContentInventoryUpdate = 1 << 7, +} diff --git a/Dalamud/Game/Gui/ChatGui.cs b/Dalamud/Game/Gui/ChatGui.cs index 6de4e3c3f..d7303c4ce 100644 --- a/Dalamud/Game/Gui/ChatGui.cs +++ b/Dalamud/Game/Gui/ChatGui.cs @@ -41,7 +41,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui private static readonly ModuleLog Log = new("ChatGui"); private readonly Queue chatQueue = new(); - private readonly Dictionary<(string PluginName, Guid CommandId), Action> dalamudLinkHandlers = new(); + private readonly Dictionary<(string PluginName, uint CommandId), Action> dalamudLinkHandlers = new(); private readonly Hook printMessageHook; private readonly Hook inventoryItemCopyHook; @@ -50,7 +50,8 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); - private ImmutableDictionary<(string PluginName, Guid CommandId), Action>? dalamudLinkHandlersCopy; + private ImmutableDictionary<(string PluginName, uint CommandId), Action>? dalamudLinkHandlersCopy; + private uint dalamudChatHandlerId = 1000; [ServiceManager.ServiceConstructor] private ChatGui() @@ -86,7 +87,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui public byte LastLinkedItemFlags { get; private set; } /// - public IReadOnlyDictionary<(string PluginName, Guid CommandId), Action> RegisteredLinkHandlers + public IReadOnlyDictionary<(string PluginName, uint CommandId), Action> RegisteredLinkHandlers { get { @@ -164,19 +165,33 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui #region Chat Links - /// - public DalamudLinkPayload AddChatLinkHandler(Action commandAction) + /// + /// Register a chat link handler. + /// + /// Internal use only. + /// The action to be executed. + /// Returns an SeString payload for the link. + public DalamudLinkPayload AddChatLinkHandler(Action commandAction) { - return this.AddChatLinkHandler("Dalamud", commandAction); + return this.AddChatLinkHandler("Dalamud", this.dalamudChatHandlerId++, commandAction); } /// - public void RemoveChatLinkHandler(Guid commandId) + /// Internal use only. + public DalamudLinkPayload AddChatLinkHandler(uint commandId, Action commandAction) + { + return this.AddChatLinkHandler("Dalamud", commandId, commandAction); + } + + /// + /// Internal use only. + public void RemoveChatLinkHandler(uint commandId) { this.RemoveChatLinkHandler("Dalamud", commandId); } /// + /// Internal use only. public void RemoveChatLinkHandler() { this.RemoveChatLinkHandler("Dalamud"); @@ -240,11 +255,11 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui /// Create a link handler. /// /// The name of the plugin handling the link. + /// The ID of the command to run. /// The command action itself. /// A payload for handling. - internal DalamudLinkPayload AddChatLinkHandler(string pluginName, Action commandAction) + internal DalamudLinkPayload AddChatLinkHandler(string pluginName, uint commandId, Action commandAction) { - var commandId = Guid.CreateVersion7(); var payload = new DalamudLinkPayload { Plugin = pluginName, CommandId = commandId }; lock (this.dalamudLinkHandlers) { @@ -277,7 +292,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui /// /// The name of the plugin handling the link. /// The ID of the command to be removed. - internal void RemoveChatLinkHandler(string pluginName, Guid commandId) + internal void RemoveChatLinkHandler(string pluginName, uint commandId) { lock (this.dalamudLinkHandlers) { @@ -400,13 +415,13 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui if (!terminatedSender.SequenceEqual(possiblyModifiedSenderData)) { - Log.Verbose($"HandlePrintMessageDetour Sender modified: {SeString.Parse(terminatedSender)} -> {parsedSender}"); + Log.Verbose($"HandlePrintMessageDetour Sender modified: {new ReadOnlySeStringSpan(terminatedSender).ToMacroString()} -> {new ReadOnlySeStringSpan(possiblyModifiedSenderData).ToMacroString()}"); sender->SetString(possiblyModifiedSenderData); } if (!terminatedMessage.SequenceEqual(possiblyModifiedMessageData)) { - Log.Verbose($"HandlePrintMessageDetour Message modified: {SeString.Parse(terminatedMessage)} -> {parsedMessage}"); + Log.Verbose($"HandlePrintMessageDetour Message modified: {new ReadOnlySeStringSpan(terminatedMessage).ToMacroString()} -> {new ReadOnlySeStringSpan(possiblyModifiedMessageData).ToMacroString()}"); message->SetString(possiblyModifiedMessageData); } @@ -534,7 +549,7 @@ internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui public byte LastLinkedItemFlags => this.chatGuiService.LastLinkedItemFlags; /// - public IReadOnlyDictionary<(string PluginName, Guid CommandId), Action> RegisteredLinkHandlers => this.chatGuiService.RegisteredLinkHandlers; + public IReadOnlyDictionary<(string PluginName, uint CommandId), Action> RegisteredLinkHandlers => this.chatGuiService.RegisteredLinkHandlers; /// void IInternalDisposableService.DisposeService() @@ -551,11 +566,11 @@ internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui } /// - public DalamudLinkPayload AddChatLinkHandler(Action commandAction) - => this.chatGuiService.AddChatLinkHandler(this.plugin.InternalName, commandAction); + public DalamudLinkPayload AddChatLinkHandler(uint commandId, Action commandAction) + => this.chatGuiService.AddChatLinkHandler(this.plugin.InternalName, commandId, commandAction); /// - public void RemoveChatLinkHandler(Guid commandId) + public void RemoveChatLinkHandler(uint commandId) => this.chatGuiService.RemoveChatLinkHandler(this.plugin.InternalName, commandId); /// diff --git a/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs b/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs index fb78e6b80..7512f4160 100644 --- a/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs +++ b/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs @@ -451,14 +451,14 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM case ContextMenuType.Default: { var ownerAddonId = ((AgentContext*)this.SelectedAgent)->OwnerAddon; - module->OpenAddon(this.AddonContextSubNameId, (uint)valueCount, values, this.SelectedAgent, 71, checked((ushort)ownerAddonId), 4); + module->OpenAddon(this.AddonContextSubNameId, (uint)valueCount, values, &this.SelectedAgent->AtkEventInterface, 71, checked((ushort)ownerAddonId), 4); break; } case ContextMenuType.Inventory: { var ownerAddonId = ((AgentInventoryContext*)this.SelectedAgent)->OwnerAddonId; - module->OpenAddon(this.AddonContextSubNameId, (uint)valueCount, values, this.SelectedAgent, 0, checked((ushort)ownerAddonId), 4); + module->OpenAddon(this.AddonContextSubNameId, (uint)valueCount, values, &this.SelectedAgent->AtkEventInterface, 0, checked((ushort)ownerAddonId), 4); break; } diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs index 8a8a4787a..2235c5ade 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBar.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs @@ -257,7 +257,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar /// The resources to remove. internal void RemoveEntry(DtrBarEntry toRemove) { - this.RemoveNode(toRemove.TextNode); + this.RemoveNode(toRemove); if (toRemove.Storage != null) { @@ -378,12 +378,12 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar var isHide = !data.Shown || data.UserHidden; var node = data.TextNode; - var nodeHidden = !node->AtkResNode.IsVisible(); + var nodeHidden = !node->IsVisible(); if (!isHide) { if (nodeHidden) - node->AtkResNode.ToggleVisibility(true); + node->ToggleVisibility(true); if (data is { Added: true, Text: not null, TextNode: not null } && (data.Dirty || nodeHidden)) { @@ -397,27 +397,27 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar ushort w = 0, h = 0; node->GetTextDrawSize(&w, &h, node->NodeText.StringPtr); - node->AtkResNode.SetWidth(w); + node->SetWidth(w); } - var elementWidth = data.TextNode->AtkResNode.Width + this.configuration.DtrSpacing; + var elementWidth = data.TextNode->Width + this.configuration.DtrSpacing; if (this.configuration.DtrSwapDirection) { - data.TextNode->AtkResNode.SetPositionFloat(runningXPos + this.configuration.DtrSpacing, 2); + data.TextNode->SetPositionFloat(runningXPos + this.configuration.DtrSpacing, 2); runningXPos += elementWidth; } else { runningXPos -= elementWidth; - data.TextNode->AtkResNode.SetPositionFloat(runningXPos, 2); + data.TextNode->SetPositionFloat(runningXPos, 2); } } else if (!nodeHidden) { // If we want the node hidden, shift it up, to prevent collision conflicts - node->AtkResNode.SetYFloat(-collisionNode->Height * dtr->RootNode->ScaleX); - node->AtkResNode.ToggleVisibility(false); + node->SetYFloat(-collisionNode->Height * dtr->RootNode->ScaleX); + node->ToggleVisibility(false); } data.Dirty = false; @@ -516,8 +516,8 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar var node = data.TextNode = this.MakeNode(++this.runningNodeIds); - this.eventHandles.TryAdd(node->AtkResNode.NodeId, new List()); - this.eventHandles[node->AtkResNode.NodeId].AddRange(new List + 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), this.uiEventManager.AddEvent(AddonEventManager.DalamudInternalKey, (nint)dtr, (nint)node, AddonEventType.MouseOut, this.DtrEventHandler), @@ -528,8 +528,8 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar while (lastChild->PrevSiblingNode != null) lastChild = lastChild->PrevSiblingNode; Log.Debug($"Found last sibling: {(ulong)lastChild:X}"); lastChild->PrevSiblingNode = (AtkResNode*)node; - node->AtkResNode.ParentNode = lastChild->ParentNode; - node->AtkResNode.NextSiblingNode = lastChild; + node->ParentNode = lastChild->ParentNode; + node->NextSiblingNode = lastChild; dtr->RootNode->ChildCount = (ushort)(dtr->RootNode->ChildCount + 1); Log.Debug("Set last sibling of DTR and updated child count"); @@ -542,22 +542,31 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar return true; } - private void RemoveNode(AtkTextNode* node) + private void RemoveNode(DtrBarEntry data) { var dtr = this.GetDtr(); + var node = data.TextNode; if (dtr == null || dtr->RootNode == null || dtr->UldManager.NodeList == null || node == null) return; - this.eventHandles[node->AtkResNode.NodeId].ForEach(handle => this.uiEventManager.RemoveEvent(AddonEventManager.DalamudInternalKey, handle)); - this.eventHandles[node->AtkResNode.NodeId].Clear(); + if (this.eventHandles.TryGetValue(node->NodeId, out var eventHandles)) + { + eventHandles.ForEach(handle => this.uiEventManager.RemoveEvent(AddonEventManager.DalamudInternalKey, handle)); + eventHandles.Clear(); + } + else + { + Log.Warning("Could not find AtkResNode with NodeId {nodeId} in eventHandles", node->NodeId); + } - var tmpPrevNode = node->AtkResNode.PrevSiblingNode; - var tmpNextNode = node->AtkResNode.NextSiblingNode; + var tmpPrevNode = node->PrevSiblingNode; + var tmpNextNode = node->NextSiblingNode; // if (tmpNextNode != null) tmpNextNode->PrevSiblingNode = tmpPrevNode; if (tmpPrevNode != null) tmpPrevNode->NextSiblingNode = tmpNextNode; - node->AtkResNode.Destroy(true); + node->Destroy(true); + data.TextNode = null; dtr->RootNode->ChildCount = (ushort)(dtr->RootNode->ChildCount - 1); Log.Debug("Set last sibling of DTR and updated child count"); @@ -575,19 +584,18 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar return null; } - newTextNode->AtkResNode.NodeId = nodeId; - newTextNode->AtkResNode.Type = NodeType.Text; - newTextNode->AtkResNode.NodeFlags = NodeFlags.AnchorLeft | NodeFlags.AnchorTop | NodeFlags.Enabled | NodeFlags.RespondToMouse | NodeFlags.HasCollision | NodeFlags.EmitsEvents; - newTextNode->AtkResNode.DrawFlags = 12; - newTextNode->AtkResNode.SetWidth(22); - newTextNode->AtkResNode.SetHeight(22); - newTextNode->AtkResNode.SetPositionFloat(-200, 2); + newTextNode->NodeId = nodeId; + newTextNode->Type = NodeType.Text; + newTextNode->NodeFlags = NodeFlags.AnchorLeft | NodeFlags.AnchorTop | NodeFlags.Enabled | NodeFlags.RespondToMouse | NodeFlags.HasCollision | NodeFlags.EmitsEvents; + newTextNode->DrawFlags = 12; + newTextNode->SetWidth(22); + newTextNode->SetHeight(22); + newTextNode->SetPositionFloat(-200, 2); newTextNode->LineSpacing = 12; newTextNode->AlignmentFontType = 5; newTextNode->FontSize = 14; - newTextNode->TextFlags = (byte)TextFlags.Edge; - newTextNode->TextFlags2 = 0; + newTextNode->TextFlags = TextFlags.Edge; if (this.emptyString == null) this.emptyString = Utf8String.FromString(" "); @@ -642,7 +650,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar break; case AddonEventType.MouseClick: - dtrBarEntry.OnClick?.Invoke(new AddonMouseEventData(eventData)); + dtrBarEntry.OnClick?.Invoke(DtrInteractionEvent.FromMouseEvent(new AddonMouseEventData(eventData))); break; } } diff --git a/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs b/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs index 54847705d..f5b7011fe 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs @@ -1,4 +1,6 @@ -using Dalamud.Configuration.Internal; +using System.Numerics; + +using Dalamud.Configuration.Internal; using Dalamud.Game.Addon.Events.EventDataTypes; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Plugin.Internal.Types; @@ -43,6 +45,16 @@ public interface IReadOnlyDtrBarEntry /// Gets a value indicating whether the user has hidden this entry from view through the Dalamud settings. /// public bool UserHidden { get; } + + /// + /// Gets an action to be invoked when the user clicks on the dtr entry. + /// + public Action? OnClick { get; } + + /// + /// Gets the axis-aligned bounding box of this entry, in screen coordinates. + /// + public (Vector2 Min, Vector2 Max) ScreenBounds { get; } } /// @@ -68,7 +80,7 @@ public interface IDtrBarEntry : IReadOnlyDtrBarEntry /// /// Gets or sets an action to be invoked when the user clicks on the dtr entry. /// - public Action? OnClick { get; set; } + public new Action? OnClick { get; set; } /// /// Remove this entry from the bar. @@ -118,7 +130,7 @@ internal sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry public SeString? Tooltip { get; set; } /// - public Action? OnClick { get; set; } + public Action? OnClick { get; set; } /// public bool HasClickAction => this.OnClick != null; @@ -141,6 +153,17 @@ internal sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry [Api13ToDo("Maybe make this config scoped to internal name?")] public bool UserHidden => this.configuration.DtrIgnore?.Contains(this.Title) ?? false; + /// + public (Vector2 Min, Vector2 Max) ScreenBounds + => this.TextNode switch + { + null => default, + var node => node->IsVisible() + ? (new(node->ScreenX, node->ScreenY), + new(node->ScreenX + node->GetWidth(), node->ScreenY + node->GetHeight())) + : default, + }; + /// /// Gets or sets the internal text node of this entry. /// diff --git a/Dalamud/Game/Gui/Dtr/DtrInteractionEvent.cs b/Dalamud/Game/Gui/Dtr/DtrInteractionEvent.cs new file mode 100644 index 000000000..8b2bd8a7d --- /dev/null +++ b/Dalamud/Game/Gui/Dtr/DtrInteractionEvent.cs @@ -0,0 +1,59 @@ +using System.Numerics; + +using Dalamud.Game.Addon.Events.EventDataTypes; + +namespace Dalamud.Game.Gui.Dtr; + +/// +/// Represents an interaction event from the DTR system. +/// +public class DtrInteractionEvent +{ + /// + /// Gets the type of mouse click (left or right). + /// + public MouseClickType ClickType { get; init; } + + /// + /// Gets the modifier keys that were held during the click. + /// + public ClickModifierKeys ModifierKeys { get; init; } + + /// + /// Gets the scroll direction of the mouse wheel, if applicable. + /// + public MouseScrollDirection ScrollDirection { get; init; } + + /// + /// Gets lower-level mouse data, if this event came from native UI. + /// + /// Can only be set by Dalamud. If null, this event was manually created. + /// + public AddonMouseEventData? AtkEventSource { get; private init; } + + /// + /// Gets the position of the mouse cursor when the event occurred. + /// + public Vector2 Position { get; init; } + + /// + /// Helper to create a from an . + /// + /// The event. + /// A better event. + public static DtrInteractionEvent FromMouseEvent(AddonMouseEventData ev) + { + return new DtrInteractionEvent + { + AtkEventSource = ev, + ClickType = ev.IsLeftClick ? MouseClickType.Left : MouseClickType.Right, + ModifierKeys = (ev.IsAltHeld ? ClickModifierKeys.Alt : 0) | + (ev.IsControlHeld ? ClickModifierKeys.Ctrl : 0) | + (ev.IsShiftHeld ? ClickModifierKeys.Shift : 0), + ScrollDirection = ev.IsScrollUp ? MouseScrollDirection.Up : + ev.IsScrollDown ? MouseScrollDirection.Down : + MouseScrollDirection.None, + Position = ev.Position, + }; + } +} diff --git a/Dalamud/Game/Gui/Dtr/DtrInteractionTypes.cs b/Dalamud/Game/Gui/Dtr/DtrInteractionTypes.cs new file mode 100644 index 000000000..7c498dc94 --- /dev/null +++ b/Dalamud/Game/Gui/Dtr/DtrInteractionTypes.cs @@ -0,0 +1,65 @@ +namespace Dalamud.Game.Gui.Dtr; + +/// +/// An enum representing the mouse click types. +/// +public enum MouseClickType +{ + /// + /// A left click. + /// + Left, + + /// + /// A right click. + /// + Right, +} + +/// +/// Modifier keys that can be held during a mouse click event. +/// +[Flags] +public enum ClickModifierKeys +{ + /// + /// No modifiers were present. + /// + None = 0, + + /// + /// The CTRL key was held. + /// + Ctrl = 1 << 0, + + /// + /// The ALT key was held. + /// + Alt = 1 << 1, + + /// + /// The SHIFT key was held. + /// + Shift = 1 << 2, +} + +/// +/// Possible directions for scroll wheel events. +/// +public enum MouseScrollDirection +{ + /// + /// No scrolling. + /// + None = 0, + + /// + /// A scroll up event. + /// + Up = 1, + + /// + /// A scroll down event. + /// + Down = -1, +} diff --git a/Dalamud/Game/Gui/GameGui.cs b/Dalamud/Game/Gui/GameGui.cs index eeb9fb572..3d17aad86 100644 --- a/Dalamud/Game/Gui/GameGui.cs +++ b/Dalamud/Game/Gui/GameGui.cs @@ -43,6 +43,7 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui private readonly Hook handleImmHook; private readonly Hook setUiVisibilityHook; private readonly Hook utf8StringFromSequenceHook; + private readonly Hook raptureAtkModuleUpdateHook; [ServiceManager.ServiceConstructor] private GameGui(TargetSigScanner sigScanner) @@ -65,6 +66,10 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui this.utf8StringFromSequenceHook = Hook.FromAddress(Utf8String.Addresses.Ctor_FromSequence.Value, this.Utf8StringFromSequenceDetour); + this.raptureAtkModuleUpdateHook = Hook.FromFunctionPointerVariable( + new(&RaptureAtkModule.StaticVirtualTablePointer->Update), + this.RaptureAtkModuleUpdateDetour); + this.handleItemHoverHook.Enable(); this.handleItemOutHook.Enable(); this.handleImmHook.Enable(); @@ -72,6 +77,7 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui this.handleActionHoverHook.Enable(); this.handleActionOutHook.Enable(); this.utf8StringFromSequenceHook.Enable(); + this.raptureAtkModuleUpdateHook.Enable(); } // Hooked delegates @@ -88,6 +94,9 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui /// public event EventHandler? HoveredActionChanged; + /// + public event Action AgentUpdate; + /// public bool GameUiHidden { get; private set; } @@ -182,6 +191,10 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui return (nint)unitManager->GetAddonByName(name, index); } + /// + public T* GetAddonByName(string name, int index = 1) where T : unmanaged + => (T*)this.GetAddonByName(name, index).Address; + /// public AgentInterfacePtr GetAgentById(int id) { @@ -234,6 +247,7 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui this.handleActionHoverHook.Dispose(); this.handleActionOutHook.Dispose(); this.utf8StringFromSequenceHook.Dispose(); + this.raptureAtkModuleUpdateHook.Dispose(); } /// @@ -276,8 +290,6 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui this.HoveredItem = itemId; this.HoveredItemChanged?.InvokeSafely(this, itemId); - - Log.Verbose($"HoveredItem changed: {itemId}"); } private AtkValue* HandleItemOutDetour(AgentItemDetail* thisPtr, AtkValue* returnValue, AtkValue* values, uint valueCount, ulong eventKind) @@ -288,22 +300,18 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui { this.HoveredItem = 0; this.HoveredItemChanged?.InvokeSafely(this, 0ul); - - Log.Verbose("HoveredItem changed: 0"); } return ret; } - private void HandleActionHoverDetour(AgentActionDetail* hoverState, FFXIVClientStructs.FFXIV.Client.UI.Agent.ActionKind actionKind, uint actionId, int a4, byte a5) + private void HandleActionHoverDetour(AgentActionDetail* hoverState, FFXIVClientStructs.FFXIV.Client.UI.Agent.ActionKind actionKind, uint actionId, int a4, bool a5, int a6, int a7) { - this.handleActionHoverHook.Original(hoverState, actionKind, actionId, a4, a5); + this.handleActionHoverHook.Original(hoverState, actionKind, actionId, a4, a5, a6, a7); this.HoveredAction.ActionKind = (HoverActionKind)actionKind; this.HoveredAction.BaseActionID = actionId; this.HoveredAction.ActionID = hoverState->ActionId; this.HoveredActionChanged?.InvokeSafely(this, this.HoveredAction); - - Log.Verbose($"HoverActionId: {actionKind}/{actionId} this:{(nint)hoverState:X}"); } private AtkValue* HandleActionOutDetour(AgentActionDetail* agentActionDetail, AtkValue* a2, AtkValue* a3, uint a4, ulong a5) @@ -320,15 +328,13 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui this.HoveredAction.BaseActionID = 0; this.HoveredAction.ActionID = 0; this.HoveredActionChanged?.InvokeSafely(this, this.HoveredAction); - - Log.Verbose("HoverActionId: 0"); } } return retVal; } - private unsafe void SetUiVisibilityDetour(RaptureAtkModule* thisPtr, bool uiVisible) + private void SetUiVisibilityDetour(RaptureAtkModule* thisPtr, bool uiVisible) { this.setUiVisibilityHook.Original(thisPtr, uiVisible); @@ -358,6 +364,21 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui return thisPtr; // this function shouldn't need to return but the original asm moves this into rax before returning so be safe? } + + private void RaptureAtkModuleUpdateDetour(RaptureAtkModule* thisPtr, float delta) + { + // The game clears the AgentUpdateFlag in the original function, but it also updates agents in it too. + // We'll make a copy of the flags, so that we can fire events after the agents have been updated. + + var agentUpdateFlag = thisPtr->AgentUpdateFlag; + + this.raptureAtkModuleUpdateHook.Original(thisPtr, delta); + + if (agentUpdateFlag != RaptureAtkModule.AgentUpdateFlags.None) + { + this.AgentUpdate.InvokeSafely((AgentUpdateFlag)agentUpdateFlag); + } + } } /// @@ -381,6 +402,7 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui this.gameGuiService.UiHideToggled += this.UiHideToggledForward; this.gameGuiService.HoveredItemChanged += this.HoveredItemForward; this.gameGuiService.HoveredActionChanged += this.HoveredActionForward; + this.gameGuiService.AgentUpdate += this.AgentUpdateForward; } /// @@ -392,6 +414,9 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui /// public event EventHandler? HoveredActionChanged; + /// + public event Action AgentUpdate; + /// public bool GameUiHidden => this.gameGuiService.GameUiHidden; @@ -411,6 +436,7 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui this.gameGuiService.UiHideToggled -= this.UiHideToggledForward; this.gameGuiService.HoveredItemChanged -= this.HoveredItemForward; this.gameGuiService.HoveredActionChanged -= this.HoveredActionForward; + this.gameGuiService.AgentUpdate -= this.AgentUpdateForward; this.UiHideToggled = null; this.HoveredItemChanged = null; @@ -441,6 +467,10 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui public AtkUnitBasePtr GetAddonByName(string name, int index = 1) => this.gameGuiService.GetAddonByName(name, index); + /// + public unsafe T* GetAddonByName(string name, int index = 1) where T : unmanaged + => (T*)this.gameGuiService.GetAddonByName(name, index).Address; + /// public AgentInterfacePtr GetAgentById(int id) => this.gameGuiService.GetAgentById(id); @@ -458,4 +488,6 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui private void HoveredItemForward(object sender, ulong itemId) => this.HoveredItemChanged?.Invoke(sender, itemId); private void HoveredActionForward(object sender, HoveredAction hoverAction) => this.HoveredActionChanged?.Invoke(sender, hoverAction); + + private void AgentUpdateForward(AgentUpdateFlag agentUpdateFlag) => this.AgentUpdate.InvokeSafely(agentUpdateFlag); } diff --git a/Dalamud/Game/Gui/NamePlate/NamePlateUpdateHandler.cs b/Dalamud/Game/Gui/NamePlate/NamePlateUpdateHandler.cs index 185be9d24..a8c6ff3c1 100644 --- a/Dalamud/Game/Gui/NamePlate/NamePlateUpdateHandler.cs +++ b/Dalamud/Game/Gui/NamePlate/NamePlateUpdateHandler.cs @@ -340,9 +340,16 @@ internal unsafe class NamePlateUpdateHandler : INamePlateUpdateHandler return null; } - return this.gameObject ??= this.context.ObjectTable[ - this.context.Ui3DModule->NamePlateObjectInfoPointers[this.ArrayIndex] - .Value->GameObject->ObjectIndex]; + if (this.ArrayIndex >= this.context.Ui3DModule->NamePlateObjectInfoCount) + return null; + + var objectInfoPtr = this.context.Ui3DModule->NamePlateObjectInfoPointers[this.ArrayIndex]; + if (objectInfoPtr.Value == null) return null; + + var gameObjectPtr = objectInfoPtr.Value->GameObject; + if (gameObjectPtr == null) return null; + + return this.gameObject ??= this.context.ObjectTable[gameObjectPtr->ObjectIndex]; } } diff --git a/Dalamud/Game/Internal/DalamudAtkTweaks.cs b/Dalamud/Game/Internal/DalamudAtkTweaks.cs index 83a2f3525..466401ef3 100644 --- a/Dalamud/Game/Internal/DalamudAtkTweaks.cs +++ b/Dalamud/Game/Internal/DalamudAtkTweaks.cs @@ -1,12 +1,12 @@ using CheapLoc; + using Dalamud.Configuration.Internal; using Dalamud.Game.Text; -using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Hooking; using Dalamud.Interface.Internal; using Dalamud.Interface.Windowing; using Dalamud.Logging.Internal; +using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI.Agent; @@ -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.HasAnyWindowSystemFocus && atkEventData != null && *(int*)atkEventData == 3 && this.configuration.IsFocusManagementEnabled) + if (eventType == AtkEventType.InputReceived && WindowSystem.ShouldInhibitAtkCloseEvents && 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.HasAnyWindowSystemFocus && this.configuration.IsFocusManagementEnabled) + if (WindowSystem.ShouldInhibitAtkCloseEvents && this.configuration.IsFocusManagementEnabled) { Log.Verbose($"Cancelling OpenSystemMenu due to WindowSystem {WindowSystem.FocusedWindowSystemNamespace}"); return; @@ -185,17 +185,23 @@ internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService secondStringEntry->ChangeType(ValueType.String); const int color = 539; - var strPlugins = new SeString().Append(new UIForegroundPayload(color)) - .Append($"{SeIconChar.BoxedLetterD.ToIconString()} ") - .Append(new UIForegroundPayload(0)) - .Append(this.locDalamudPlugins).Encode(); - var strSettings = new SeString().Append(new UIForegroundPayload(color)) - .Append($"{SeIconChar.BoxedLetterD.ToIconString()} ") - .Append(new UIForegroundPayload(0)) - .Append(this.locDalamudSettings).Encode(); - firstStringEntry->SetManagedString(strPlugins); - secondStringEntry->SetManagedString(strSettings); + using var rssb = new RentedSeStringBuilder(); + + firstStringEntry->SetManagedString(rssb.Builder + .PushColorType(color) + .Append($"{SeIconChar.BoxedLetterD.ToIconString()} ") + .PopColorType() + .Append(this.locDalamudPlugins) + .GetViewAsSpan()); + + rssb.Builder.Clear(); + secondStringEntry->SetManagedString(rssb.Builder + .PushColorType(color) + .Append($"{SeIconChar.BoxedLetterD.ToIconString()} ") + .PopColorType() + .Append(this.locDalamudSettings) + .GetViewAsSpan()); // open menu with new size var sizeEntry = &atkValueArgs[4]; diff --git a/Dalamud/Game/Internal/DalamudCompletion.cs b/Dalamud/Game/Internal/DalamudCompletion.cs index ec5652b3c..e3564c823 100644 --- a/Dalamud/Game/Internal/DalamudCompletion.cs +++ b/Dalamud/Game/Internal/DalamudCompletion.cs @@ -114,7 +114,7 @@ internal sealed unsafe class DalamudCompletion : IInternalDisposableService this.ResetCompletionData(); this.ClearCachedCommands(); - var currentText = component->UnkText1.StringPtr.ExtractText(); + var currentText = component->EvaluatedString.StringPtr.ExtractText(); var commands = this.commandManager.Commands .Where(kv => kv.Value.ShowInHelp && (currentText.Length == 0 || kv.Key.StartsWith(currentText))) @@ -195,7 +195,7 @@ internal sealed unsafe class DalamudCompletion : IInternalDisposableService component = (AtkComponentTextInput*)componentBase; - addon = component->ContainingAddon; + addon = component->OwnerAddon; if (addon == null) addon = component->ContainingAddon2; diff --git a/Dalamud/Game/Inventory/GameInventory.cs b/Dalamud/Game/Inventory/GameInventory.cs index 5c8ed27b0..535b84372 100644 --- a/Dalamud/Game/Inventory/GameInventory.cs +++ b/Dalamud/Game/Inventory/GameInventory.cs @@ -2,16 +2,14 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using Dalamud.Game.Gui; using Dalamud.Game.Inventory.InventoryEventArgTypes; -using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Logging.Internal; using Dalamud.Plugin.Internal; using Dalamud.Plugin.Services; -using FFXIVClientStructs.FFXIV.Client.UI; - namespace Dalamud.Game.Inventory; /// @@ -33,7 +31,8 @@ internal class GameInventory : IInternalDisposableService [ServiceManager.ServiceDependency] private readonly Framework framework = Service.Get(); - private readonly Hook raptureAtkModuleUpdateHook; + [ServiceManager.ServiceDependency] + private readonly GameGui gameGui = Service.Get(); private readonly GameInventoryType[] inventoryTypes; private readonly GameInventoryItem[]?[] inventoryItems; @@ -47,18 +46,9 @@ internal class GameInventory : IInternalDisposableService this.inventoryTypes = Enum.GetValues(); this.inventoryItems = new GameInventoryItem[this.inventoryTypes.Length][]; - unsafe - { - this.raptureAtkModuleUpdateHook = Hook.FromFunctionPointerVariable( - new(&RaptureAtkModule.StaticVirtualTablePointer->Update), - this.RaptureAtkModuleUpdateDetour); - } - - this.raptureAtkModuleUpdateHook.Enable(); + this.gameGui.AgentUpdate += this.OnAgentUpdate; } - private unsafe delegate void RaptureAtkModuleUpdateDelegate(RaptureAtkModule* ram, float f1); - /// void IInternalDisposableService.DisposeService() { @@ -68,7 +58,7 @@ internal class GameInventory : IInternalDisposableService this.subscribersPendingChange.Clear(); this.subscribersChanged = false; this.framework.Update -= this.OnFrameworkUpdate; - this.raptureAtkModuleUpdateHook.Dispose(); + this.gameGui.AgentUpdate -= this.OnAgentUpdate; } } @@ -319,10 +309,9 @@ internal class GameInventory : IInternalDisposableService return items; } - private unsafe void RaptureAtkModuleUpdateDetour(RaptureAtkModule* ram, float f1) + private void OnAgentUpdate(AgentUpdateFlag agentUpdateFlag) { - this.inventoriesMightBeChanged |= ram->AgentUpdateFlag != 0; - this.raptureAtkModuleUpdateHook.Original(ram, f1); + this.inventoriesMightBeChanged |= true; } /// diff --git a/Dalamud/Game/Network/Internal/NetworkHandlers.cs b/Dalamud/Game/Network/Internal/NetworkHandlers.cs index 7d6304655..6a6d73b33 100644 --- a/Dalamud/Game/Network/Internal/NetworkHandlers.cs +++ b/Dalamud/Game/Network/Internal/NetworkHandlers.cs @@ -11,18 +11,18 @@ using Dalamud.Game.Gui; using Dalamud.Game.Network.Internal.MarketBoardUploaders; using Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis; using Dalamud.Game.Network.Structures; +using Dalamud.Game.Player; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Hooking; using Dalamud.Networking.Http; using Dalamud.Utility; -using FFXIVClientStructs.FFXIV.Client.Game.Control; using FFXIVClientStructs.FFXIV.Client.Game.InstanceContent; -using FFXIVClientStructs.FFXIV.Client.Game.UI; using FFXIVClientStructs.FFXIV.Client.Network; -using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Client.UI.Info; + using Lumina.Excel.Sheets; + using Serilog; namespace Dalamud.Game.Network.Internal; @@ -269,29 +269,8 @@ internal unsafe class NetworkHandlers : IInternalDisposableService private static (ulong UploaderId, uint WorldId) GetUploaderInfo() { - var agentLobby = AgentLobby.Instance(); - - var uploaderId = agentLobby->LobbyData.ContentId; - if (uploaderId == 0) - { - var playerState = PlayerState.Instance(); - if (playerState->IsLoaded == 1) - { - uploaderId = playerState->ContentId; - } - } - - var worldId = agentLobby->LobbyData.CurrentWorldId; - if (worldId == 0) - { - var localPlayer = Control.GetLocalPlayer(); - if (localPlayer != null) - { - worldId = localPlayer->CurrentWorld; - } - } - - return (uploaderId, worldId); + var playerState = Service.Get(); + return (playerState.ContentId, playerState.CurrentWorld.RowId); } private unsafe nint CfPopDetour(PublicContentDirector.EnterContentInfoPacket* packetData) diff --git a/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs b/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs index 18c48b67d..9cd46f798 100644 --- a/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs +++ b/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs @@ -16,6 +16,6 @@ internal class NetworkHandlersAddressResolver : BaseAddressResolver { this.CustomTalkEventResponsePacketHandler = scanner.ScanText( - "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC ?? 49 8B D9 41 0F B6 F8 0F B7 F2 8B E9 E8 ?? ?? ?? ?? 48 8B C8 44 0F B6 CF 0F B6 44 24 ?? 44 0F B7 C6 88 44 24 ?? 8B D5 48 89 5C 24"); // unnamed in CS + "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC ?? 49 8B D9 41 0F B6 F8 0F B7 F2 8B E9 E8 ?? ?? ?? ?? 44 0F B6 54 24 ?? 44 0F B6 CF 44 88 54 24 ?? 44 0F B7 C6 8B D5"); // unnamed in CS } } diff --git a/Dalamud/Game/Player/MentorVersion.cs b/Dalamud/Game/Player/MentorVersion.cs new file mode 100644 index 000000000..e856e1169 --- /dev/null +++ b/Dalamud/Game/Player/MentorVersion.cs @@ -0,0 +1,27 @@ +namespace Dalamud.Game.Player; + +/// +/// Specifies the mentor certification version for a player. +/// +public enum MentorVersion : byte +{ + /// + /// Indicates that the player has never held mentor status in any expansion. + /// + None = 0, + + /// + /// Indicates that the player was last a mentor during the Shadowbringers expansion. + /// + Shadowbringers = 1, + + /// + /// Indicates that the player was last a mentor during the Endwalker expansion. + /// + Endwalker = 2, + + /// + /// Indicates that the player was last a mentor during the Dawntrail expansion. + /// + Dawntrail = 3, +} diff --git a/Dalamud/Game/Player/PlayerAttribute.cs b/Dalamud/Game/Player/PlayerAttribute.cs new file mode 100644 index 000000000..9d9954817 --- /dev/null +++ b/Dalamud/Game/Player/PlayerAttribute.cs @@ -0,0 +1,489 @@ +namespace Dalamud.Game.Player; + +/// +/// Represents a player's attribute. +/// +public enum PlayerAttribute +{ + /// + /// Strength. + /// + /// + /// Affects physical damage dealt by gladiator's arms, marauder's arms, dark knight's arms, gunbreaker's arms, pugilist's arms, lancer's arms, samurai's arms, reaper's arms, thaumaturge's arms, arcanist's arms, red mage's arms, pictomancer's arms, conjurer's arms, astrologian's arms, sage's arms, and blue mage's arms. + /// + Strength = 1, + + /// + /// Dexterity. + /// + /// + /// Affects physical damage dealt by rogue's arms, viper's arms, archer's arms, machinist's arms, and dancer's arms. + /// + Dexterity = 2, + + /// + /// Vitality. + /// + /// + /// Affects maximum HP. + /// + Vitality = 3, + + /// + /// Intelligence. + /// + /// + /// Affects attack magic potency when role is DPS. + /// + Intelligence = 4, + + /// + /// Mind. + /// + /// + /// Affects healing magic potency. Also affects attack magic potency when role is Healer. + /// + Mind = 5, + + /// + /// Piety. + /// + /// + /// Affects MP regeneration. Regeneration rate is determined by piety. Only applicable when in battle and role is Healer. + /// + Piety = 6, + + /// + /// Health Points. + /// + HP = 7, + + /// + /// Mana Points. + /// + MP = 8, + + /// + /// Tactical Points. + /// + TP = 9, + + /// + /// Gathering Point. + /// + GP = 10, + + /// + /// Crafting Points. + /// + CP = 11, + + /// + /// Physical Damage. + /// + PhysicalDamage = 12, + + /// + /// Magic Damage. + /// + MagicDamage = 13, + + /// + /// Delay. + /// + Delay = 14, + + /// + /// Additional Effect. + /// + AdditionalEffect = 15, + + /// + /// Attack Speed. + /// + AttackSpeed = 16, + + /// + /// Block Rate. + /// + BlockRate = 17, + + /// + /// Block Strength. + /// + BlockStrength = 18, + + /// + /// Tenacity. + /// + /// + /// Affects the amount of physical and magic damage dealt and received, as well as HP restored. The higher the value, the more damage dealt, the more HP restored, and the less damage taken. Only applicable when role is Tank. + /// + Tenacity = 19, + + /// + /// Attack Power. + /// + /// + /// Affects amount of damage dealt by physical attacks. The higher the value, the more damage dealt. + /// + AttackPower = 20, + + /// + /// Defense. + /// + /// + /// Affects the amount of damage taken by physical attacks. The higher the value, the less damage taken. + /// + Defense = 21, + + /// + /// Direct Hit Rate. + /// + /// + /// Affects the rate at which your physical and magic attacks land direct hits, dealing slightly more damage than normal hits. The higher the value, the higher the frequency with which your hits will be direct. Higher values will also result in greater damage for actions which guarantee direct hits. + /// + DirectHitRate = 22, + + /// + /// Evasion. + /// + Evasion = 23, + + /// + /// Magic Defense. + /// + /// + /// Affects the amount of damage taken by magic attacks. The higher the value, the less damage taken. + /// + MagicDefense = 24, + + /// + /// Critical Hit Power. + /// + CriticalHitPower = 25, + + /// + /// Critical Hit Resilience. + /// + CriticalHitResilience = 26, + + /// + /// Critical Hit. + /// + /// + /// Affects the amount of physical and magic damage dealt, as well as HP restored. The higher the value, the higher the frequency with which your hits will be critical/higher the potency of critical hits. + /// + CriticalHit = 27, + + /// + /// Critical Hit Evasion. + /// + CriticalHitEvasion = 28, + + /// + /// Slashing Resistance. + /// + /// + /// Decreases damage done by slashing attacks. + /// + SlashingResistance = 29, + + /// + /// Piercing Resistance. + /// + /// + /// Decreases damage done by piercing attacks. + /// + PiercingResistance = 30, + + /// + /// Blunt Resistance. + /// + /// + /// Decreases damage done by blunt attacks. + /// + BluntResistance = 31, + + /// + /// Projectile Resistance. + /// + ProjectileResistance = 32, + + /// + /// Attack Magic Potency. + /// + /// + /// Affects the amount of damage dealt by magic attacks. + /// + AttackMagicPotency = 33, + + /// + /// Healing Magic Potency. + /// + /// + /// Affects the amount of HP restored via healing magic. + /// + HealingMagicPotency = 34, + + /// + /// Enhancement Magic Potency. + /// + EnhancementMagicPotency = 35, + + /// + /// Elemental Bonus. + /// + ElementalBonus = 36, + + /// + /// Fire Resistance. + /// + /// + /// Decreases fire-aspected damage. + /// + FireResistance = 37, + + /// + /// Ice Resistance. + /// + /// + /// Decreases ice-aspected damage. + /// + IceResistance = 38, + + /// + /// Wind Resistance. + /// + /// + /// Decreases wind-aspected damage. + /// + WindResistance = 39, + + /// + /// Earth Resistance. + /// + /// + /// Decreases earth-aspected damage. + /// + EarthResistance = 40, + + /// + /// Lightning Resistance. + /// + /// + /// Decreases lightning-aspected damage. + /// + LightningResistance = 41, + + /// + /// Water Resistance. + /// + /// + /// Decreases water-aspected damage. + /// + WaterResistance = 42, + + /// + /// Magic Resistance. + /// + MagicResistance = 43, + + /// + /// Determination. + /// + /// + /// Affects the amount of damage dealt by both physical and magic attacks, as well as the amount of HP restored by healing spells. + /// + Determination = 44, + + /// + /// Skill Speed. + /// + /// + /// Affects both the casting and recast timers, as well as the damage over time potency for weaponskills and auto-attacks. The higher the value, the shorter the timers/higher the potency. + /// + SkillSpeed = 45, + + /// + /// Spell Speed. + /// + /// + /// Affects both the casting and recast timers for spells. The higher the value, the shorter the timers. Also affects a spell's damage over time or healing over time potency. + /// + SpellSpeed = 46, + + /// + /// Haste. + /// + Haste = 47, + + /// + /// Morale. + /// + /// + /// In PvP, replaces physical and magical defense in determining damage inflicted by other players. Also influences the amount of damage dealt to other players. + /// + Morale = 48, + + /// + /// Enmity. + /// + Enmity = 49, + + /// + /// Enmity Reduction. + /// + EnmityReduction = 50, + + /// + /// Desynthesis Skill Gain. + /// + DesynthesisSkillGain = 51, + + /// + /// EXP Bonus. + /// + EXPBonus = 52, + + /// + /// Regen. + /// + Regen = 53, + + /// + /// Special Attribute. + /// + SpecialAttribute = 54, + + /// + /// Main Attribute. + /// + MainAttribute = 55, + + /// + /// Secondary Attribute. + /// + SecondaryAttribute = 56, + + /// + /// Slow Resistance. + /// + /// + /// Shortens the duration of slow. + /// + SlowResistance = 57, + + /// + /// Petrification Resistance. + /// + PetrificationResistance = 58, + + /// + /// Paralysis Resistance. + /// + ParalysisResistance = 59, + + /// + /// Silence Resistance. + /// + /// + /// Shortens the duration of silence. + /// + SilenceResistance = 60, + + /// + /// Blind Resistance. + /// + /// + /// Shortens the duration of blind. + /// + BlindResistance = 61, + + /// + /// Poison Resistance. + /// + /// + /// Shortens the duration of poison. + /// + PoisonResistance = 62, + + /// + /// Stun Resistance. + /// + /// + /// Shortens the duration of stun. + /// + StunResistance = 63, + + /// + /// Sleep Resistance. + /// + /// + /// Shortens the duration of sleep. + /// + SleepResistance = 64, + + /// + /// Bind Resistance. + /// + /// + /// Shortens the duration of bind. + /// + BindResistance = 65, + + /// + /// Heavy Resistance. + /// + /// + /// Shortens the duration of heavy. + /// + HeavyResistance = 66, + + /// + /// Doom Resistance. + /// + DoomResistance = 67, + + /// + /// Reduced Durability Loss. + /// + ReducedDurabilityLoss = 68, + + /// + /// Increased Spiritbond Gain. + /// + IncreasedSpiritbondGain = 69, + + /// + /// Craftsmanship. + /// + /// + /// Affects the amount of progress achieved in a single synthesis step. + /// + Craftsmanship = 70, + + /// + /// Control. + /// + /// + /// Affects the amount of quality improved in a single synthesis step. + /// + Control = 71, + + /// + /// Gathering. + /// + /// + /// Affects the rate at which items are gathered. + /// + Gathering = 72, + + /// + /// Perception. + /// + /// + /// Affects item yield when gathering as a botanist or miner, and the size of fish when fishing or spearfishing. + /// + Perception = 73, +} diff --git a/Dalamud/Game/Player/PlayerState.cs b/Dalamud/Game/Player/PlayerState.cs new file mode 100644 index 000000000..bd19b5bfb --- /dev/null +++ b/Dalamud/Game/Player/PlayerState.cs @@ -0,0 +1,232 @@ +using System.Collections.Generic; + +using Dalamud.Data; +using Dalamud.IoC; +using Dalamud.IoC.Internal; +using Dalamud.Plugin.Services; + +using FFXIVClientStructs.FFXIV.Client.UI.Agent; + +using Lumina.Excel; +using Lumina.Excel.Sheets; + +using CSPlayerState = FFXIVClientStructs.FFXIV.Client.Game.UI.PlayerState; +using GrandCompany = Lumina.Excel.Sheets.GrandCompany; + +namespace Dalamud.Game.Player; + +/// +/// This class contains the PlayerState wrappers. +/// +[PluginInterface] +[ServiceManager.EarlyLoadedService] +[ResolveVia] +internal unsafe class PlayerState : IServiceType, IPlayerState +{ + [ServiceManager.ServiceConstructor] + private PlayerState() + { + } + + /// + public bool IsLoaded => CSPlayerState.Instance()->IsLoaded; + + /// + public string CharacterName => this.IsLoaded ? CSPlayerState.Instance()->CharacterNameString : string.Empty; + + /// + public uint EntityId => this.IsLoaded ? CSPlayerState.Instance()->EntityId : default; + + /// + public ulong ContentId => this.IsLoaded ? CSPlayerState.Instance()->ContentId : default; + + /// + public RowRef CurrentWorld + { + get + { + var agentLobby = AgentLobby.Instance(); + return agentLobby->IsLoggedIn + ? LuminaUtils.CreateRef(agentLobby->LobbyData.CurrentWorldId) + : default; + } + } + + /// + public RowRef HomeWorld + { + get + { + var agentLobby = AgentLobby.Instance(); + return agentLobby->IsLoggedIn + ? LuminaUtils.CreateRef(agentLobby->LobbyData.HomeWorldId) + : default; + } + } + + /// + public Sex Sex => this.IsLoaded ? (Sex)CSPlayerState.Instance()->Sex : default; + + /// + public RowRef Race => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->Race) : default; + + /// + public RowRef Tribe => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->Tribe) : default; + + /// + 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 bool IsLevelSynced => this.IsLoaded && CSPlayerState.Instance()->IsLevelSynced; + + /// + public short EffectiveLevel => this.IsLoaded ? (this.IsLevelSynced ? CSPlayerState.Instance()->SyncedLevel : CSPlayerState.Instance()->CurrentLevel) : default; + + /// + public RowRef GuardianDeity => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->GuardianDeity) : default; + + /// + public byte BirthMonth => this.IsLoaded ? CSPlayerState.Instance()->BirthMonth : default; + + /// + public byte BirthDay => this.IsLoaded ? CSPlayerState.Instance()->BirthDay : default; + + /// + public RowRef FirstClass => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->FirstClass) : default; + + /// + public RowRef StartTown => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->StartTown) : default; + + /// + public int BaseStrength => this.IsLoaded ? CSPlayerState.Instance()->BaseStrength : default; + + /// + public int BaseDexterity => this.IsLoaded ? CSPlayerState.Instance()->BaseDexterity : default; + + /// + public int BaseVitality => this.IsLoaded ? CSPlayerState.Instance()->BaseVitality : default; + + /// + public int BaseIntelligence => this.IsLoaded ? CSPlayerState.Instance()->BaseIntelligence : default; + + /// + public int BaseMind => this.IsLoaded ? CSPlayerState.Instance()->BaseMind : default; + + /// + public int BasePiety => this.IsLoaded ? CSPlayerState.Instance()->BasePiety : default; + + /// + public RowRef GrandCompany => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->GrandCompany) : default; + + /// + public RowRef HomeAetheryte => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->HomeAetheryteId) : default; + + /// + public IReadOnlyList> FavoriteAetherytes + { + get + { + var playerState = CSPlayerState.Instance(); + if (!playerState->IsLoaded) + return default; + + var count = playerState->FavouriteAetheryteCount; + if (count == 0) + return default; + + var array = new RowRef[count]; + + for (var i = 0; i < count; i++) + array[i] = LuminaUtils.CreateRef(playerState->FavouriteAetherytes[i]); + + return array; + } + } + + /// + public RowRef FreeAetheryte => this.IsLoaded ? LuminaUtils.CreateRef(CSPlayerState.Instance()->FreeAetheryteId) : default; + + /// + public uint BaseRestedExperience => this.IsLoaded ? CSPlayerState.Instance()->BaseRestedExperience : default; + + /// + public short PlayerCommendations => this.IsLoaded ? CSPlayerState.Instance()->PlayerCommendations : default; + + /// + public byte DeliveryLevel => this.IsLoaded ? CSPlayerState.Instance()->DeliveryLevel : default; + + /// + public MentorVersion MentorVersion => this.IsLoaded ? (MentorVersion)CSPlayerState.Instance()->MentorVersion : default; + + /// + public bool IsMentor => this.IsLoaded && CSPlayerState.Instance()->IsMentor(); + + /// + public bool IsBattleMentor => this.IsLoaded && CSPlayerState.Instance()->IsBattleMentor(); + + /// + public bool IsTradeMentor => this.IsLoaded && CSPlayerState.Instance()->IsTradeMentor(); + + /// + public bool IsNovice => this.IsLoaded && CSPlayerState.Instance()->IsNovice(); + + /// + public bool IsReturner => this.IsLoaded && CSPlayerState.Instance()->IsReturner(); + + /// + public int GetAttribute(PlayerAttribute attribute) => this.IsLoaded ? CSPlayerState.Instance()->Attributes[(int)attribute] : default; + + /// + public byte GetGrandCompanyRank(GrandCompany grandCompany) + { + if (!this.IsLoaded) + return default; + + return grandCompany.RowId switch + { + 1 => CSPlayerState.Instance()->GCRankMaelstrom, + 2 => CSPlayerState.Instance()->GCRankTwinAdders, + 3 => CSPlayerState.Instance()->GCRankImmortalFlames, + _ => default, + }; + } + + /// + public short GetClassJobLevel(ClassJob classJob) + { + if (classJob.ExpArrayIndex == -1) + return default; + + if (!this.IsLoaded) + return default; + + return CSPlayerState.Instance()->ClassJobLevels[classJob.ExpArrayIndex]; + } + + /// + public int GetClassJobExperience(ClassJob classJob) + { + if (classJob.ExpArrayIndex == -1) + return default; + + if (!this.IsLoaded) + return default; + + return CSPlayerState.Instance()->ClassJobExperience[classJob.ExpArrayIndex]; + } + + /// + public float GetDesynthesisLevel(ClassJob classJob) + { + if (classJob.DohDolJobIndex == -1) + return default; + + if (!this.IsLoaded) + return default; + + return CSPlayerState.Instance()->DesynthesisLevels[classJob.DohDolJobIndex] / 100f; + } +} diff --git a/Dalamud/Game/Player/Sex.cs b/Dalamud/Game/Player/Sex.cs new file mode 100644 index 000000000..0981cb9a4 --- /dev/null +++ b/Dalamud/Game/Player/Sex.cs @@ -0,0 +1,17 @@ +namespace Dalamud.Game.Player; + +/// +/// Represents the sex of a character. +/// +public enum Sex : byte +{ + /// + /// Male sex. + /// + Male = 0, + + /// + /// Female sex. + /// + Female = 1, +} diff --git a/Dalamud/Game/SigScanner.cs b/Dalamud/Game/SigScanner.cs index 77d312aec..c8a371aee 100644 --- a/Dalamud/Game/SigScanner.cs +++ b/Dalamud/Game/SigScanner.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; + using Iced.Intel; using Newtonsoft.Json; using Serilog; @@ -325,7 +326,7 @@ public class SigScanner : IDisposable, ISigScanner } /// - public nint[] ScanAllText(string signature) => this.ScanAllText(signature, default).ToArray(); + public nint[] ScanAllText(string signature) => this.ScanAllText(signature, CancellationToken.None).ToArray(); /// public IEnumerable ScanAllText(string signature, CancellationToken cancellationToken) @@ -337,16 +338,16 @@ public class SigScanner : IDisposable, ISigScanner { cancellationToken.ThrowIfCancellationRequested(); - var index = IndexOf(mBase, this.TextSectionSize, needle, mask, badShift); + var index = IndexOf(mBase, this.TextSectionSize - (int)(mBase - this.TextSectionBase), needle, mask, badShift); if (index < 0) break; var scanRet = mBase + index; + mBase = scanRet + 1; if (this.IsCopy) scanRet -= this.moduleCopyOffset; yield return scanRet; - mBase = scanRet + 1; } } diff --git a/Dalamud/Game/Text/Evaluator/Internal/SheetRedirectResolver.cs b/Dalamud/Game/Text/Evaluator/Internal/SheetRedirectResolver.cs index 262e7ad21..523086f48 100644 --- a/Dalamud/Game/Text/Evaluator/Internal/SheetRedirectResolver.cs +++ b/Dalamud/Game/Text/Evaluator/Internal/SheetRedirectResolver.cs @@ -221,8 +221,8 @@ internal class SheetRedirectResolver : IServiceType sheetName = nameof(LSheets.AkatsukiNoteString); colIndex = 0; - if (this.dataManager.Excel.GetSubrowSheet().TryGetRow(rowId, out var row)) - rowId = (uint)row[0].Unknown2; + if (this.dataManager.Excel.GetSubrowSheet().TryGetSubrow(rowId, 0, out var row)) + rowId = row.ListName.RowId; break; } } diff --git a/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs b/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs index 624b62253..58bcdbd0b 100644 --- a/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs +++ b/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs @@ -8,6 +8,7 @@ using Dalamud.Configuration.Internal; using Dalamud.Data; using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.Config; +using Dalamud.Game.Player; using Dalamud.Game.Text.Evaluator.Internal; using Dalamud.Game.Text.Noun; using Dalamud.Game.Text.Noun.Enums; @@ -35,7 +36,6 @@ using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; using AddonSheet = Lumina.Excel.Sheets.Addon; -using PlayerState = FFXIVClientStructs.FFXIV.Client.Game.UI.PlayerState; using StatusSheet = Lumina.Excel.Sheets.Status; namespace Dalamud.Game.Text.Evaluator; @@ -68,6 +68,9 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator [ServiceManager.ServiceDependency] private readonly SheetRedirectResolver sheetRedirectResolver = Service.Get(); + [ServiceManager.ServiceDependency] + private readonly PlayerState playerState = Service.Get(); + private readonly ConcurrentDictionary, string> actStrCache = []; private readonly ConcurrentDictionary, string> objStrCache = []; @@ -121,6 +124,15 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator return this.Evaluate(ReadOnlySeString.FromMacroString(macroString).AsSpan(), localParameters, language); } + /// + public ReadOnlySeString EvaluateMacroString( + ReadOnlySpan macroString, + Span localParameters = default, + ClientLanguage? language = null) + { + return this.Evaluate(ReadOnlySeString.FromMacroString(macroString).AsSpan(), localParameters, language); + } + /// public ReadOnlySeString EvaluateFromAddon( uint addonId, @@ -247,6 +259,9 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator 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); @@ -315,6 +330,9 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator 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); @@ -447,6 +465,29 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator return false; } + private bool TryResolveSwitchPlatform(in SeStringContext context, in ReadOnlySePayloadSpan payload) + { + if (!payload.TryGetExpression(out var expr1)) + return false; + + if (!expr1.TryGetInt(out var intVal)) + return false; + + // Our version of the game uses IsMacClient() here and the + // Xbox version seems to always return 7 for the platform. + var platform = Util.IsWine() ? 5 : 3; + + // The sheet is seeminly split into first 20 rows for wired controllers + // and the last 20 rows for wireless controllers. + var rowId = (uint)((20 * ((intVal - 1) / 20)) + (platform - 4 < 2 ? 2 : 1)); + + if (!this.dataManager.GetExcelSheet().TryGetRow(rowId, out var platformRow)) + return false; + + context.Builder.Append(platformRow.Name); + return true; + } + private unsafe bool TryResolvePcName(in SeStringContext context, in ReadOnlySePayloadSpan payload) { if (!payload.TryGetExpression(out var eEntityId)) @@ -526,7 +567,7 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator return false; // the game uses LocalPlayer here, but using PlayerState seems more safe. - return this.ResolveStringExpression(in context, PlayerState.Instance()->EntityId == entityId ? eTrue : eFalse); + return this.ResolveStringExpression(in context, this.playerState.EntityId == entityId ? eTrue : eFalse); } private bool TryResolveColor(in SeStringContext context, in ReadOnlySePayloadSpan payload) @@ -757,6 +798,65 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator return true; } + private bool TryResolveSheetSub(in SeStringContext context, in ReadOnlySePayloadSpan payload) + { + var enu = payload.GetEnumerator(); + + if (!enu.MoveNext() || !enu.Current.TryGetString(out var eSheetNameStr)) + return false; + + if (!enu.MoveNext() || !this.TryResolveUInt(in context, enu.Current, out var eRowIdValue)) + return false; + + if (!enu.MoveNext() || !this.TryResolveUInt(in context, enu.Current, out var eSubrowIdValue)) + return false; + + if (!enu.MoveNext() || !this.TryResolveUInt(in context, enu.Current, out var eColIndexValue)) + return false; + + var secondaryRowId = this.GetSubrowSheetIntValue(context.Language, eSheetNameStr.ExtractText(), eRowIdValue, (ushort)eSubrowIdValue, eColIndexValue); + if (secondaryRowId == -1) + return false; + + if (!enu.MoveNext() || !enu.Current.TryGetString(out var eSecondarySheetNameStr)) + return false; + + if (!enu.MoveNext() || !this.TryResolveUInt(in context, enu.Current, out var secondaryColIndex)) + return false; + + var text = this.FormatSheetValue(context.Language, eSecondarySheetNameStr.ExtractText(), (uint)secondaryRowId, secondaryColIndex, 0); + if (text.IsEmpty) + return false; + + this.CreateSheetLink(context, eSecondarySheetNameStr.ExtractText(), text, eRowIdValue, eSubrowIdValue); + + return true; + } + + private int GetSubrowSheetIntValue(ClientLanguage language, string sheetName, uint rowId, ushort subrowId, uint colIndex) + { + if (!this.dataManager.Excel.SheetNames.Contains(sheetName)) + return -1; + + if (!this.dataManager.GetSubrowExcelSheet(language, sheetName) + .TryGetSubrow(rowId, subrowId, out var row)) + return -1; + + if (colIndex >= row.Columns.Count) + return -1; + + var column = row.Columns[(int)colIndex]; + return column.Type switch + { + ExcelColumnDataType.Int8 => row.ReadInt8(column.Offset), + ExcelColumnDataType.UInt8 => row.ReadUInt8(column.Offset), + ExcelColumnDataType.Int16 => row.ReadInt16(column.Offset), + ExcelColumnDataType.UInt16 => row.ReadUInt16(column.Offset), + ExcelColumnDataType.Int32 => row.ReadInt32(column.Offset), + _ => -1, + }; + } + private ReadOnlySeString FormatSheetValue(ClientLanguage language, string sheetName, uint rowId, uint colIndex, uint colParam) { if (!this.dataManager.Excel.SheetNames.Contains(sheetName)) @@ -1532,23 +1632,63 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator return true; var isNoun = false; - var col = 0; - if (ranges.StartsWith("noun")) - { - isNoun = true; - } - else if (ranges.StartsWith("col")) - { - var colRangeEnd = ranges.IndexOf(','); - if (colRangeEnd == -1) - colRangeEnd = ranges.Length; + var colIndex = 0; + Span cols = stackalloc int[8]; + cols.Clear(); + var hasRanges = false; + var isInRange = false; - col = int.Parse(ranges[4..colRangeEnd]); - } - else if (ranges.StartsWith("tail")) + while (!string.IsNullOrWhiteSpace(ranges)) + { + // find the end of the current entry + var entryEnd = ranges.IndexOf(','); + if (entryEnd == -1) + entryEnd = ranges.Length; + + var entry = ranges.AsSpan(0, entryEnd); + + if (ranges.StartsWith("noun", StringComparison.Ordinal)) + { + isNoun = true; + } + else if (ranges.StartsWith("col", StringComparison.Ordinal) && colIndex < cols.Length) + { + cols[colIndex++] = int.Parse(entry[4..]); + } + else if (ranges.StartsWith("tail", StringComparison.Ordinal)) + { + // currently not supported, since there are no known uses + context.Builder.Append(payload); + return false; + } + else + { + var dash = entry.IndexOf('-'); + + hasRanges |= true; + + if (dash == -1) + { + isInRange |= int.Parse(entry) == rowId; + } + else + { + isInRange |= rowId >= int.Parse(entry[..dash]) + && rowId <= int.Parse(entry[(dash + 1)..]); + } + } + + // if it's the end of the string, we're done + if (entryEnd == ranges.Length) + break; + + // else, move to the next entry + ranges = ranges[(entryEnd + 1)..].TrimStart(); + } + + if (hasRanges && !isInRange) { - // couldn't find any, so we don't handle them :p context.Builder.Append(payload); return false; } @@ -1566,7 +1706,23 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator } else if (this.dataManager.GetExcelSheet(context.Language, sheetName).TryGetRow(rowId, out var row)) { - context.Builder.Append(row.ReadStringColumn(col)); + if (colIndex == 0) + { + context.Builder.Append(row.ReadStringColumn(0)); + return true; + } + else + { + for (var i = 0; i < colIndex; i++) + { + var text = row.ReadStringColumn(cols[i]); + if (!text.IsEmpty) + { + context.Builder.Append(text); + break; + } + } + } } return true; @@ -1910,7 +2066,7 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator value = (uint)MacroDecoder.GetMacroTime()->tm_mday; return true; case ExpressionType.Weekday: - value = (uint)MacroDecoder.GetMacroTime()->tm_wday; + value = (uint)MacroDecoder.GetMacroTime()->tm_wday + 1; return true; case ExpressionType.Month: value = (uint)MacroDecoder.GetMacroTime()->tm_mon + 1; diff --git a/Dalamud/Game/Text/Evaluator/SeStringParameter.cs b/Dalamud/Game/Text/Evaluator/SeStringParameter.cs index c1f238f56..1c6dd96cb 100644 --- a/Dalamud/Game/Text/Evaluator/SeStringParameter.cs +++ b/Dalamud/Game/Text/Evaluator/SeStringParameter.cs @@ -71,9 +71,12 @@ 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); + + public static implicit operator SeStringParameter(ReadOnlySpan value) => new(value); } diff --git a/Dalamud/Game/Text/Noun/NounParams.cs b/Dalamud/Game/Text/Noun/NounParams.cs index 3d5c424be..ab7a732d2 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 6B 08" - nameof(LSheets.BeastTribe) => 10, + // See "E8 ?? ?? ?? ?? 44 8B 66 ?? 8B E8" + nameof(LSheets.BeastTribe) => 11, nameof(LSheets.DeepDungeonItem) => 1, nameof(LSheets.DeepDungeonEquipment) => 1, nameof(LSheets.DeepDungeonMagicStone) => 1, diff --git a/Dalamud/Game/Text/SeStringHandling/BitmapFontIcon.cs b/Dalamud/Game/Text/SeStringHandling/BitmapFontIcon.cs index 427e7902a..8dca7c4ce 100644 --- a/Dalamud/Game/Text/SeStringHandling/BitmapFontIcon.cs +++ b/Dalamud/Game/Text/SeStringHandling/BitmapFontIcon.cs @@ -784,4 +784,9 @@ public enum BitmapFontIcon : uint /// The Cross-World Party Member icon. /// CrossWorldPartyMember = 181, + + /// + /// The Event Tutorial icon. + /// + EventTutorial = 182, } diff --git a/Dalamud/Game/Text/SeStringHandling/Payload.cs b/Dalamud/Game/Text/SeStringHandling/Payload.cs index 7131a88a7..c797c4a91 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payload.cs @@ -2,11 +2,8 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; -using Dalamud.Data; using Dalamud.Game.Text.SeStringHandling.Payloads; -using Dalamud.Plugin.Services; -using Newtonsoft.Json; using Serilog; // TODOs: @@ -117,7 +114,7 @@ public abstract partial class Payload var chunkType = (SeStringChunkType)reader.ReadByte(); var chunkLen = GetInteger(reader); - var packetStart = reader.BaseStream.Position; + var expressionsStart = reader.BaseStream.Position; // any unhandled payload types will be turned into a RawPayload with the exact same binary data switch (chunkType) @@ -208,11 +205,10 @@ public abstract partial class Payload } payload ??= new RawPayload((byte)chunkType); - payload.DecodeImpl(reader, reader.BaseStream.Position + chunkLen - 1); + payload.DecodeImpl(reader, reader.BaseStream.Position + chunkLen); - // read through the rest of the packet - var readBytes = (uint)(reader.BaseStream.Position - packetStart); - reader.ReadBytes((int)(chunkLen - readBytes + 1)); // +1 for the END_BYTE marker + // skip to the end of the payload, in case the specific payload handler didn't read everything + reader.BaseStream.Seek(expressionsStart + chunkLen + 1, SeekOrigin.Begin); // +1 for the END_BYTE marker return payload; } diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/AutoTranslatePayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/AutoTranslatePayload.cs index b038deb6f..470e942c3 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/AutoTranslatePayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/AutoTranslatePayload.cs @@ -1,14 +1,11 @@ -using System.Collections.Generic; using System.IO; -using System.Linq; -using Dalamud.Data; +using Dalamud.Game.Text.Evaluator; -using Lumina.Excel.Sheets; +using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; using Newtonsoft.Json; -using Serilog; namespace Dalamud.Game.Text.SeStringHandling.Payloads; @@ -17,7 +14,7 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads; /// public class AutoTranslatePayload : Payload, ITextProvider { - private string? text; + private ReadOnlySeString payload; /// /// Initializes a new instance of the class. @@ -34,6 +31,14 @@ public class AutoTranslatePayload : Payload, ITextProvider // TODO: friendlier ctor? not sure how to handle that given how weird the tables are this.Group = group; this.Key = key; + + 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); } /// @@ -41,6 +46,7 @@ public class AutoTranslatePayload : Payload, ITextProvider /// internal AutoTranslatePayload() { + this.payload = default; // parsed by DecodeImpl } /// @@ -68,8 +74,13 @@ public class AutoTranslatePayload : Payload, ITextProvider { get { - // wrap the text in the colored brackets that is uses in-game, since those are not actually part of any of the payloads - return this.text ??= $"{(char)SeIconChar.AutoTranslateOpen} {this.Resolve()} {(char)SeIconChar.AutoTranslateClose}"; + if (this.Group is 100 or 200) + { + return Service.Get().Evaluate(this.payload).ToString(); + } + + // wrap the text in the colored brackets that are used in-game, since those are not actually part of any of the fixed macro payload + return $"{(char)SeIconChar.AutoTranslateOpen} {Service.Get().Evaluate(this.payload)} {(char)SeIconChar.AutoTranslateClose}"; } } @@ -85,95 +96,25 @@ public class AutoTranslatePayload : Payload, ITextProvider /// protected override byte[] EncodeImpl() { - var keyBytes = MakeInteger(this.Key); - - var chunkLen = keyBytes.Length + 2; - var bytes = new List() - { - START_BYTE, - (byte)SeStringChunkType.AutoTranslateKey, (byte)chunkLen, - (byte)this.Group, - }; - bytes.AddRange(keyBytes); - bytes.Add(END_BYTE); - - return bytes.ToArray(); + return this.payload.Data.ToArray(); } /// protected override void DecodeImpl(BinaryReader reader, long endOfStream) { - // this seems to always be a bare byte, and not following normal integer encoding - // the values in the table are all <70 so this is presumably ok - this.Group = reader.ReadByte(); + var body = reader.ReadBytes((int)(endOfStream - reader.BaseStream.Position)); + var rosps = new ReadOnlySePayloadSpan(ReadOnlySePayloadType.Macro, MacroCode.Fixed, body.AsSpan()); - this.Key = GetInteger(reader); - } + var span = rosps.EnvelopeByteLength <= 512 ? stackalloc byte[rosps.EnvelopeByteLength] : new byte[rosps.EnvelopeByteLength]; + rosps.WriteEnvelopeTo(span); + this.payload = new ReadOnlySeString(span); - private static ReadOnlySeString ResolveTextCommand(TextCommand command) - { - // TextCommands prioritize the `Alias` field, if it not empty - // Example for this is /rangerpose2l which becomes /blackrangerposeb in chat - return !command.Alias.IsEmpty ? command.Alias : command.Command; - } - - private string Resolve() - { - string value = null; - - var excelModule = Service.Get().Excel; - var completionSheet = excelModule.GetSheet(); - - // try to get the row in the Completion table itself, because this is 'easiest' - // The row may not exist at all (if the Key is for another table), or it could be the wrong row - // (again, if it's meant for another table) - - if (completionSheet.GetRowOrDefault(this.Key) is { } completion && completion.Group == this.Group) + if (rosps.TryGetExpression(out var expr1, out var expr2) + && expr1.TryGetUInt(out var group) + && expr2.TryGetUInt(out var key)) { - // if the row exists in this table and the group matches, this is actually the correct data - value = completion.Text.ExtractText(); + this.Group = group + 1; + this.Key = key; } - else - { - try - { - // we need to get the linked table and do the lookup there instead - // in this case, there will only be one entry for this group id - var row = completionSheet.First(r => r.Group == this.Group); - // many of the names contain valid id ranges after the table name, but we don't need those - var actualTableName = row.LookupTable.ExtractText().Split('[')[0]; - - var name = actualTableName switch - { - "Action" => excelModule.GetSheet().GetRow(this.Key).Name, - "ActionComboRoute" => excelModule.GetSheet().GetRow(this.Key).Name, - "BuddyAction" => excelModule.GetSheet().GetRow(this.Key).Name, - "ClassJob" => excelModule.GetSheet().GetRow(this.Key).Name, - "Companion" => excelModule.GetSheet().GetRow(this.Key).Singular, - "CraftAction" => excelModule.GetSheet().GetRow(this.Key).Name, - "GeneralAction" => excelModule.GetSheet().GetRow(this.Key).Name, - "GuardianDeity" => excelModule.GetSheet().GetRow(this.Key).Name, - "MainCommand" => excelModule.GetSheet().GetRow(this.Key).Name, - "Mount" => excelModule.GetSheet().GetRow(this.Key).Singular, - "Pet" => excelModule.GetSheet().GetRow(this.Key).Name, - "PetAction" => excelModule.GetSheet().GetRow(this.Key).Name, - "PetMirage" => excelModule.GetSheet().GetRow(this.Key).Name, - "PlaceName" => excelModule.GetSheet().GetRow(this.Key).Name, - "Race" => excelModule.GetSheet().GetRow(this.Key).Masculine, - "TextCommand" => AutoTranslatePayload.ResolveTextCommand(excelModule.GetSheet().GetRow(this.Key)), - "Tribe" => excelModule.GetSheet().GetRow(this.Key).Masculine, - "Weather" => excelModule.GetSheet().GetRow(this.Key).Name, - _ => throw new Exception(actualTableName), - }; - - value = name.ExtractText(); - } - catch (Exception e) - { - Log.Error(e, $"AutoTranslatePayload - failed to resolve: {this.Type} - Group: {this.Group}, Key: {this.Key}"); - } - } - - return value; } } diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/DalamudLinkPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/DalamudLinkPayload.cs index ee06172b4..8b020b111 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/DalamudLinkPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/DalamudLinkPayload.cs @@ -16,7 +16,7 @@ public class DalamudLinkPayload : Payload public override PayloadType Type => PayloadType.DalamudLink; /// Gets the plugin command ID to be linked. - public Guid CommandId { get; internal set; } + public uint CommandId { get; internal set; } /// Gets an optional extra integer value 1. public int Extra1 { get; internal set; } @@ -40,7 +40,7 @@ public class DalamudLinkPayload : Payload var ssb = Lumina.Text.SeStringBuilder.SharedPool.Get(); var res = ssb.BeginMacro(MacroCode.Link) .AppendIntExpression((int)EmbeddedInfoType.DalamudLink - 1) - .AppendStringExpression(this.CommandId.ToString()) + .AppendUIntExpression(this.CommandId) .AppendIntExpression(this.Extra1) .AppendIntExpression(this.Extra2) .BeginStringExpression() @@ -72,15 +72,15 @@ public class DalamudLinkPayload : Payload if (!pluginExpression.TryGetString(out var pluginString)) return; - if (!commandIdExpression.TryGetString(out var commandId)) + if (!commandIdExpression.TryGetUInt(out var commandId)) return; this.Plugin = pluginString.ExtractText(); - this.CommandId = Guid.Parse(commandId.ExtractText()); + this.CommandId = commandId; } else { - if (!commandIdExpression.TryGetString(out var commandId)) + if (!commandIdExpression.TryGetUInt(out var commandId)) return; if (!extra1Expression.TryGetInt(out var extra1)) @@ -102,7 +102,7 @@ public class DalamudLinkPayload : Payload return; } - this.CommandId = Guid.Parse(commandId.ExtractText()); + this.CommandId = commandId; this.Extra1 = extra1; this.Extra2 = extra2; this.Plugin = extraData[0]; diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/RawPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/RawPayload.cs index a7e41cbc6..02a7c113e 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/RawPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/RawPayload.cs @@ -95,14 +95,12 @@ public class RawPayload : Payload /// protected override byte[] EncodeImpl() { - var chunkLen = this.data.Length + 1; - var bytes = new List() { START_BYTE, this.chunkType, - (byte)chunkLen, }; + bytes.AddRange(MakeInteger((uint)this.data.Length)); // chunkLen bytes.AddRange(this.data); bytes.Add(END_BYTE); @@ -113,6 +111,6 @@ public class RawPayload : Payload /// protected override void DecodeImpl(BinaryReader reader, long endOfStream) { - this.data = reader.ReadBytes((int)(endOfStream - reader.BaseStream.Position + 1)); + this.data = reader.ReadBytes((int)(endOfStream - reader.BaseStream.Position)); } } diff --git a/Dalamud/Game/Text/SeStringHandling/SeString.cs b/Dalamud/Game/Text/SeStringHandling/SeString.cs index a30ad9bbe..8805c2177 100644 --- a/Dalamud/Game/Text/SeStringHandling/SeString.cs +++ b/Dalamud/Game/Text/SeStringHandling/SeString.cs @@ -118,6 +118,7 @@ public class SeString /// /// 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(); /// diff --git a/Dalamud/Game/UnlockState/ItemActionType.cs b/Dalamud/Game/UnlockState/ItemActionType.cs new file mode 100644 index 000000000..8e3d79b84 --- /dev/null +++ b/Dalamud/Game/UnlockState/ItemActionType.cs @@ -0,0 +1,95 @@ +using Lumina.Excel.Sheets; + +namespace Dalamud.Game.UnlockState; + +/// +/// Enum for . +/// +internal enum ItemActionType : ushort +{ + /// + /// No item action. + /// + None = 0, + + /// + /// Unlocks a companion (minion). + /// + Companion = 853, + + /// + /// Unlocks a chocobo companion barding. + /// + BuddyEquip = 1013, + + /// + /// Unlocks a mount. + /// + Mount = 1322, + + /// + /// Unlocks recipes from a crafting recipe book. + /// + SecretRecipeBook = 2136, + + /// + /// Unlocks various types of content (e.g. Riding Maps, Blue Mage Totems, Emotes, Hairstyles). + /// + UnlockLink = 2633, + + /// + /// Unlocks a Triple Triad Card. + /// + TripleTriadCard = 3357, + + /// + /// Unlocks gathering nodes of a Folklore Tome. + /// + FolkloreTome = 4107, + + /// + /// Unlocks an Orchestrion Roll. + /// + OrchestrionRoll = 25183, + + /// + /// Unlocks portrait designs. + /// + FramersKit = 29459, + + /// + /// Unlocks Bozjan Field Notes. + /// + /// These are server-side but are cached client-side. + FieldNotes = 19743, + + /// + /// Unlocks an Ornament (fashion accessory). + /// + Ornament = 20086, + + /// + /// Unlocks Glasses. + /// + Glasses = 37312, + + /// + /// Company Seal Vouchers, which convert the item into Company Seals when used. + /// + CompanySealVouchers = 41120, + + /// + /// Unlocks Occult Records in Occult Crescent. + /// + OccultRecords = 43141, + + /// + /// Unlocks Phantom Jobs in Occult Crescent. + /// + SoulShards = 43142, + + /// + /// Star Contributor Certificate, which grants the Star Contributor status in Cosmic Exploration. + /// + StarContributorCertificate = 45189, +} diff --git a/Dalamud/Game/UnlockState/RecipeData.cs b/Dalamud/Game/UnlockState/RecipeData.cs new file mode 100644 index 000000000..c419ba4fd --- /dev/null +++ b/Dalamud/Game/UnlockState/RecipeData.cs @@ -0,0 +1,283 @@ +using System.Linq; + +using CommunityToolkit.HighPerformance; + +using Dalamud.Data; +using Dalamud.Game.Gui; + +using FFXIVClientStructs.FFXIV.Client.Game; +using FFXIVClientStructs.FFXIV.Client.Game.UI; +using FFXIVClientStructs.Interop; + +using Lumina.Excel.Sheets; + +namespace Dalamud.Game.UnlockState; + +/// +/// Represents recipe-related data for all crafting classes. +/// +[ServiceManager.EarlyLoadedService] +internal unsafe class RecipeData : IInternalDisposableService +{ + [ServiceManager.ServiceDependency] + private readonly DataManager dataManager = Service.Get(); + + [ServiceManager.ServiceDependency] + private readonly ClientState.ClientState clientState = Service.Get(); + + [ServiceManager.ServiceDependency] + private readonly GameGui gameGui = Service.Get(); + + private readonly ushort[] craftTypeLevels; + private readonly byte[] unlockedNoteBookDivisionsCount; + private readonly byte[] unlockedSecretNoteBookDivisionsCount; + private readonly ushort[,] noteBookDivisionIds; + private byte[]? cachedUnlockedSecretRecipeBooks; + private byte[]? cachedUnlockLinks; + private byte[]? cachedCompletedQuests; + + /// + /// Initializes a new instance of the class. + /// + [ServiceManager.ServiceConstructor] + public RecipeData() + { + var numCraftTypes = this.dataManager.GetExcelSheet().Count(); + var numSecretNotBookDivisions = this.dataManager.GetExcelSheet().Count(row => row.RowId is >= 1000 and < 2000); + + this.unlockedNoteBookDivisionsCount = new byte[numCraftTypes]; + this.unlockedSecretNoteBookDivisionsCount = new byte[numCraftTypes]; + this.noteBookDivisionIds = new ushort[numCraftTypes, numSecretNotBookDivisions]; + + this.craftTypeLevels = new ushort[numCraftTypes]; + + this.clientState.Login += this.Update; + this.clientState.Logout += this.OnLogout; + this.clientState.LevelChanged += this.OnlevelChanged; + this.gameGui.AgentUpdate += this.OnAgentUpdate; + } + + /// + void IInternalDisposableService.DisposeService() + { + this.clientState.Login -= this.Update; + this.clientState.Logout -= this.OnLogout; + this.clientState.LevelChanged -= this.OnlevelChanged; + this.gameGui.AgentUpdate -= this.OnAgentUpdate; + } + + /// + /// Determines whether the specified Recipe is unlocked. + /// + /// The Recipe row to check. + /// if unlocked; otherwise, . + public bool IsRecipeUnlocked(Recipe row) + { + // E8 ?? ?? ?? ?? 48 63 76 (2025.09.04) + var division = row.RecipeNotebookList.RowId != 0 && row.RecipeNotebookList.IsValid + ? (row.RecipeNotebookList.RowId - 1000) / 8 + 1000 + : ((uint)row.RecipeLevelTable.Value.ClassJobLevel - 1) / 5; + + // E8 ?? ?? ?? ?? 33 ED 84 C0 75 (2025.09.04) + foreach (var craftTypeRow in this.dataManager.GetExcelSheet()) + { + var craftType = (byte)craftTypeRow.RowId; + + if (division < this.unlockedNoteBookDivisionsCount[craftType]) + return true; + + if (this.unlockedNoteBookDivisionsCount[craftType] == 0) + continue; + + if (division is 5000 or 5001) + return true; + + if (division < 1000) + continue; + + if (this.unlockedSecretNoteBookDivisionsCount[craftType] == 0) + continue; + + if (this.noteBookDivisionIds.GetRowSpan(craftType).Contains((ushort)division)) + return true; + } + + return false; + } + + private void OnLogout(int type, int code) + { + this.cachedUnlockedSecretRecipeBooks = null; + this.cachedUnlockLinks = null; + this.cachedCompletedQuests = null; + } + + private void OnlevelChanged(uint classJobId, uint level) + { + if (this.dataManager.GetExcelSheet().TryGetRow(classJobId, out var classJobRow) && + classJobRow.ClassJobCategory.RowId == 33) // Crafter + { + this.Update(); + } + } + + private void OnAgentUpdate(AgentUpdateFlag agentUpdateFlag) + { + if (agentUpdateFlag.HasFlag(AgentUpdateFlag.UnlocksUpdate)) + this.Update(); + } + + private void Update() + { + // based on Client::Game::UI::RecipeNote.InitializeStructs + + if (!this.clientState.IsLoggedIn || !this.NeedsUpdate()) + return; + + Array.Clear(this.unlockedNoteBookDivisionsCount, 0, this.unlockedNoteBookDivisionsCount.Length); + Array.Clear(this.unlockedSecretNoteBookDivisionsCount, 0, this.unlockedSecretNoteBookDivisionsCount.Length); + Array.Clear(this.noteBookDivisionIds, 0, this.noteBookDivisionIds.Length); + + foreach (var craftTypeRow in this.dataManager.GetExcelSheet()) + { + var craftType = (byte)craftTypeRow.RowId; + var craftTypeLevel = RecipeNote.Instance()->GetCraftTypeLevel(craftType); + if (craftTypeLevel == 0) + continue; + + var noteBookDivisionIndex = -1; + + foreach (var noteBookDivisionRow in this.dataManager.GetExcelSheet()) + { + if (noteBookDivisionRow.RowId < 1000) + { + if (craftTypeLevel >= noteBookDivisionRow.CraftOpeningLevel) + this.unlockedNoteBookDivisionsCount[craftType]++; + } + else if (noteBookDivisionRow.RowId < 2000) + { + noteBookDivisionIndex++; + + // 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; + + // For future Lumina.Excel update, replace with: + // if (notebookDivisionRow.RequiresSecretRecipeBookGroupUnlock) + if (noteBookDivisionRow.Unknown1) + { + var secretRecipeBookUnlocked = false; + + // For future Lumina.Excel update, iterate over notebookDivisionRow.SecretRecipeBookGroups + for (var i = 0; i < 2; i++) + { + // 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; + + 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; + } + } + + if (noteBookDivisionRow.CraftOpeningLevel > craftTypeLevel && !secretRecipeBookUnlocked) + continue; + } + else if (craftTypeLevel < noteBookDivisionRow.CraftOpeningLevel) + { + continue; + } + else if (noteBookDivisionRow.QuestUnlock.RowId != 0 && !UIState.Instance()->IsUnlockLinkUnlockedOrQuestCompleted(noteBookDivisionRow.QuestUnlock.RowId)) + { + continue; + } + + this.unlockedSecretNoteBookDivisionsCount[craftType]++; + this.noteBookDivisionIds[craftType, noteBookDivisionIndex] = (ushort)noteBookDivisionRow.RowId; + } + } + } + } + + private bool NeedsUpdate() + { + var changed = false; + + foreach (var craftTypeRow in this.dataManager.GetExcelSheet()) + { + var craftType = (byte)craftTypeRow.RowId; + var craftTypeLevel = RecipeNote.Instance()->GetCraftTypeLevel(craftType); + + if (this.craftTypeLevels[craftType] != craftTypeLevel) + { + this.craftTypeLevels[craftType] = craftTypeLevel; + changed |= true; + } + } + + if (this.cachedUnlockedSecretRecipeBooks == null || !PlayerState.Instance()->UnlockedSecretRecipeBooks.SequenceEqual(this.cachedUnlockedSecretRecipeBooks)) + { + this.cachedUnlockedSecretRecipeBooks = PlayerState.Instance()->UnlockedSecretRecipeBooks.ToArray(); + changed |= true; + } + + if (this.cachedUnlockLinks == null || !UIState.Instance()->UnlockLinks.SequenceEqual(this.cachedUnlockLinks)) + { + this.cachedUnlockLinks = UIState.Instance()->UnlockLinks.ToArray(); + changed |= true; + } + + if (this.cachedCompletedQuests == null || !QuestManager.Instance()->CompletedQuests.SequenceEqual(this.cachedCompletedQuests)) + { + this.cachedCompletedQuests = QuestManager.Instance()->CompletedQuests.ToArray(); + changed |= true; + } + + return changed; + } +} diff --git a/Dalamud/Game/UnlockState/UnlockState.cs b/Dalamud/Game/UnlockState/UnlockState.cs new file mode 100644 index 000000000..cd896ffb6 --- /dev/null +++ b/Dalamud/Game/UnlockState/UnlockState.cs @@ -0,0 +1,858 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; + +using Dalamud.Data; +using Dalamud.Game.Gui; +using Dalamud.IoC; +using Dalamud.IoC.Internal; +using Dalamud.Logging.Internal; +using Dalamud.Plugin.Services; + +using FFXIVClientStructs.FFXIV.Client.Game; +using FFXIVClientStructs.FFXIV.Client.Game.InstanceContent; +using FFXIVClientStructs.FFXIV.Client.Game.UI; +using FFXIVClientStructs.FFXIV.Component.Exd; + +using Lumina.Excel; +using Lumina.Excel.Sheets; + +using ActionSheet = Lumina.Excel.Sheets.Action; +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. +/// +[ServiceManager.EarlyLoadedService] +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(); + + [ServiceManager.ServiceDependency] + private readonly ClientState.ClientState clientState = Service.Get(); + + [ServiceManager.ServiceDependency] + private readonly GameGui gameGui = Service.Get(); + + [ServiceManager.ServiceDependency] + private readonly RecipeData recipeData = Service.Get(); + + [ServiceManager.ServiceConstructor] + private UnlockState() + { + this.clientState.Login += this.OnLogin; + this.clientState.Logout += this.OnLogout; + this.gameGui.AgentUpdate += this.OnAgentUpdate; + } + + /// + public event IUnlockState.UnlockDelegate Unlock; + + private bool IsLoaded => PlayerState.Instance()->IsLoaded; + + /// + void IInternalDisposableService.DisposeService() + { + this.clientState.Login -= this.OnLogin; + this.clientState.Logout -= this.OnLogout; + this.gameGui.AgentUpdate -= this.OnAgentUpdate; + } + + /// + public bool IsActionUnlocked(ActionSheet row) + { + return this.IsUnlockLinkUnlocked(row.UnlockLink.RowId); + } + + /// + public bool IsAetherCurrentUnlocked(AetherCurrent row) + { + if (!this.IsLoaded) + return false; + + return PlayerState.Instance()->IsAetherCurrentUnlocked(row.RowId); + } + + /// + public bool IsAetherCurrentCompFlgSetUnlocked(AetherCurrentCompFlgSet row) + { + if (!this.IsLoaded) + return false; + + return PlayerState.Instance()->IsAetherCurrentZoneComplete(row.RowId); + } + + /// + public bool IsAozActionUnlocked(AozAction row) + { + if (!this.IsLoaded) + return false; + + if (row.RowId == 0 || !row.Action.IsValid) + return false; + + return UIState.Instance()->IsUnlockLinkUnlockedOrQuestCompleted(row.Action.Value.UnlockLink.RowId); + } + + /// + public bool IsBannerBgUnlocked(BannerBg row) + { + return row.UnlockCondition.IsValid && this.IsBannerConditionUnlocked(row.UnlockCondition.Value); + } + + /// + public bool IsBannerConditionUnlocked(BannerCondition row) + { + if (row.RowId == 0) + return false; + + if (!this.IsLoaded) + return false; + + var rowPtr = ExdModule.GetBannerConditionByIndex(row.RowId); + if (rowPtr == null) + return false; + + return ExdModule.GetBannerConditionUnlockState(rowPtr) == 0; + } + + /// + public bool IsBannerDecorationUnlocked(BannerDecoration row) + { + return row.UnlockCondition.IsValid && this.IsBannerConditionUnlocked(row.UnlockCondition.Value); + } + + /// + public bool IsBannerFacialUnlocked(BannerFacial row) + { + return row.UnlockCondition.IsValid && this.IsBannerConditionUnlocked(row.UnlockCondition.Value); + } + + /// + public bool IsBannerFrameUnlocked(BannerFrame row) + { + return row.UnlockCondition.IsValid && this.IsBannerConditionUnlocked(row.UnlockCondition.Value); + } + + /// + public bool IsBannerTimelineUnlocked(BannerTimeline row) + { + return row.UnlockCondition.IsValid && this.IsBannerConditionUnlocked(row.UnlockCondition.Value); + } + + /// + public bool IsBuddyActionUnlocked(BuddyAction row) + { + return this.IsUnlockLinkUnlocked(row.UnlockLink); + } + + /// + public bool IsBuddyEquipUnlocked(BuddyEquip row) + { + if (!this.IsLoaded) + return false; + + return UIState.Instance()->Buddy.CompanionInfo.IsBuddyEquipUnlocked(row.RowId); + } + + /// + public bool IsCharaMakeCustomizeUnlocked(CharaMakeCustomize row) + { + return row.IsPurchasable && this.IsUnlockLinkUnlocked(row.UnlockLink); + } + + /// + public bool IsChocoboTaxiStandUnlocked(ChocoboTaxiStand row) + { + if (!this.IsLoaded) + return false; + + return UIState.Instance()->IsChocoboTaxiStandUnlocked(row.RowId); + } + + /// + public bool IsCompanionUnlocked(Companion row) + { + if (!this.IsLoaded) + return false; + + return UIState.Instance()->IsCompanionUnlocked(row.RowId); + } + + /// + public bool IsCraftActionUnlocked(CraftAction row) + { + return this.IsUnlockLinkUnlocked(row.QuestRequirement.RowId); + } + + /// + public bool IsCSBonusContentTypeUnlocked(CSBonusContentType row) + { + return this.IsUnlockLinkUnlocked(row.UnlockLink); + } + + /// + public bool IsEmoteUnlocked(Emote row) + { + return this.IsUnlockLinkUnlocked(row.UnlockLink); + } + + /// + public bool IsEmjVoiceNpcUnlocked(EmjVoiceNpc row) + { + return this.IsUnlockLinkUnlocked(row.Unknown26); + } + + /// + public bool IsEmjCostumeUnlocked(EmjCostume row) + { + return this.dataManager.GetExcelSheet().TryGetRow(row.RowId, out var emjVoiceNpcRow) + && this.IsEmjVoiceNpcUnlocked(emjVoiceNpcRow) + && QuestManager.IsQuestComplete(row.Unknown1); + } + + /// + public bool IsGeneralActionUnlocked(GeneralAction row) + { + return this.IsUnlockLinkUnlocked(row.UnlockLink); + } + + /// + public bool IsGlassesUnlocked(Glasses row) + { + if (!this.IsLoaded) + return false; + + return PlayerState.Instance()->IsGlassesUnlocked((ushort)row.RowId); + } + + /// + public bool IsHowToUnlocked(HowTo row) + { + if (!this.IsLoaded) + return false; + + return UIState.Instance()->IsHowToUnlocked(row.RowId); + } + + /// + public bool IsInstanceContentUnlocked(InstanceContentSheet row) + { + if (!this.IsLoaded) + return false; + + return UIState.IsInstanceContentUnlocked(row.RowId); + } + + /// + public unsafe bool IsItemUnlocked(Item row) + { + if (row.ItemAction.RowId == 0) + return false; + + if (!this.IsLoaded) + return false; + + // 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 ((ItemActionType)row.ItemAction.Value.Type) + { + case ItemActionType.Companion: + return UIState.Instance()->IsCompanionUnlocked(row.ItemAction.Value.Data[0]); + + case ItemActionType.BuddyEquip: + return UIState.Instance()->Buddy.CompanionInfo.IsBuddyEquipUnlocked(row.ItemAction.Value.Data[0]); + + case ItemActionType.Mount: + return PlayerState.Instance()->IsMountUnlocked(row.ItemAction.Value.Data[0]); + + case ItemActionType.SecretRecipeBook: + return PlayerState.Instance()->IsSecretRecipeBookUnlocked(row.ItemAction.Value.Data[0]); + + case ItemActionType.UnlockLink: + case ItemActionType.OccultRecords: + return UIState.Instance()->IsUnlockLinkUnlocked(row.ItemAction.Value.Data[0]); + + case ItemActionType.TripleTriadCard when row.AdditionalData.Is(): + return UIState.Instance()->IsTripleTriadCardUnlocked((ushort)row.AdditionalData.RowId); + + case ItemActionType.FolkloreTome: + return PlayerState.Instance()->IsFolkloreBookUnlocked(row.ItemAction.Value.Data[0]); + + case ItemActionType.OrchestrionRoll when row.AdditionalData.Is(): + return PlayerState.Instance()->IsOrchestrionRollUnlocked(row.AdditionalData.RowId); + + case ItemActionType.FramersKit: + return PlayerState.Instance()->IsFramersKitUnlocked(row.AdditionalData.RowId); + + case ItemActionType.Ornament: + return PlayerState.Instance()->IsOrnamentUnlocked(row.ItemAction.Value.Data[0]); + + case ItemActionType.Glasses: + return PlayerState.Instance()->IsGlassesUnlocked((ushort)row.AdditionalData.RowId); + + 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 ItemActionType.CompanySealVouchers: + return false; + } + + var nativeRow = ExdModule.GetItemRowById(row.RowId); + return nativeRow != null && UIState.Instance()->IsItemActionUnlocked(nativeRow) == 1; + } + + /// + public bool IsMcGuffinUnlocked(McGuffin row) + { + return PlayerState.Instance()->IsMcGuffinUnlocked(row.RowId); + } + + /// + public bool IsMJILandmarkUnlocked(MJILandmark row) + { + return this.IsUnlockLinkUnlocked(row.UnlockLink); + } + + /// + public bool IsMKDLoreUnlocked(MKDLore row) + { + return this.IsUnlockLinkUnlocked(row.Unknown2); + } + + /// + public bool IsMountUnlocked(Mount row) + { + if (!this.IsLoaded) + return false; + + return PlayerState.Instance()->IsMountUnlocked(row.RowId); + } + + /// + public bool IsNotebookDivisionUnlocked(NotebookDivision row) + { + return this.IsUnlockLinkUnlocked(row.QuestUnlock.RowId); + } + + /// + public bool IsOrchestrionUnlocked(Orchestrion row) + { + if (!this.IsLoaded) + return false; + + return PlayerState.Instance()->IsOrchestrionRollUnlocked(row.RowId); + } + + /// + public bool IsOrnamentUnlocked(Ornament row) + { + if (!this.IsLoaded) + return false; + + return PlayerState.Instance()->IsOrnamentUnlocked(row.RowId); + } + + /// + public bool IsPerformUnlocked(Perform row) + { + return this.IsUnlockLinkUnlocked((uint)row.UnlockLink); + } + + /// + public bool IsPublicContentUnlocked(PublicContentSheet row) + { + if (!this.IsLoaded) + return false; + + return UIState.IsPublicContentUnlocked(row.RowId); + } + + /// + public bool IsRecipeUnlocked(Recipe row) + { + return this.recipeData.IsRecipeUnlocked(row); + } + + /// + public bool IsSecretRecipeBookUnlocked(SecretRecipeBook row) + { + if (!this.IsLoaded) + return false; + + return PlayerState.Instance()->IsSecretRecipeBookUnlocked(row.RowId); + } + + /// + public bool IsTraitUnlocked(Trait row) + { + return this.IsUnlockLinkUnlocked(row.Quest.RowId); + } + + /// + public bool IsTripleTriadCardUnlocked(TripleTriadCard row) + { + if (!this.IsLoaded) + return false; + + return UIState.Instance()->IsTripleTriadCardUnlocked((ushort)row.RowId); + } + + /// + public bool IsItemUnlockable(Item row) + { + if (row.ItemAction.RowId == 0) + return false; + + 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; + } + + /// + public bool IsRowRefUnlocked(RowRef rowRef) where T : struct, IExcelRow + { + return this.IsRowRefUnlocked((RowRef)rowRef); + } + + /// + public bool IsRowRefUnlocked(RowRef rowRef) + { + if (!this.IsLoaded || rowRef.IsUntyped) + return false; + + if (rowRef.TryGetValue(out var actionRow)) + return this.IsActionUnlocked(actionRow); + + if (rowRef.TryGetValue(out var aetherCurrentRow)) + return this.IsAetherCurrentUnlocked(aetherCurrentRow); + + if (rowRef.TryGetValue(out var aetherCurrentCompFlgSetRow)) + return this.IsAetherCurrentCompFlgSetUnlocked(aetherCurrentCompFlgSetRow); + + if (rowRef.TryGetValue(out var aozActionRow)) + return this.IsAozActionUnlocked(aozActionRow); + + if (rowRef.TryGetValue(out var bannerBgRow)) + return this.IsBannerBgUnlocked(bannerBgRow); + + if (rowRef.TryGetValue(out var bannerConditionRow)) + return this.IsBannerConditionUnlocked(bannerConditionRow); + + if (rowRef.TryGetValue(out var bannerDecorationRow)) + return this.IsBannerDecorationUnlocked(bannerDecorationRow); + + if (rowRef.TryGetValue(out var bannerFacialRow)) + return this.IsBannerFacialUnlocked(bannerFacialRow); + + if (rowRef.TryGetValue(out var bannerFrameRow)) + return this.IsBannerFrameUnlocked(bannerFrameRow); + + if (rowRef.TryGetValue(out var bannerTimelineRow)) + return this.IsBannerTimelineUnlocked(bannerTimelineRow); + + if (rowRef.TryGetValue(out var buddyActionRow)) + return this.IsBuddyActionUnlocked(buddyActionRow); + + if (rowRef.TryGetValue(out var buddyEquipRow)) + return this.IsBuddyEquipUnlocked(buddyEquipRow); + + if (rowRef.TryGetValue(out var csBonusContentTypeRow)) + return this.IsCSBonusContentTypeUnlocked(csBonusContentTypeRow); + + if (rowRef.TryGetValue(out var charaMakeCustomizeRow)) + return this.IsCharaMakeCustomizeUnlocked(charaMakeCustomizeRow); + + if (rowRef.TryGetValue(out var chocoboTaxiStandRow)) + return this.IsChocoboTaxiStandUnlocked(chocoboTaxiStandRow); + + if (rowRef.TryGetValue(out var companionRow)) + return this.IsCompanionUnlocked(companionRow); + + if (rowRef.TryGetValue(out var craftActionRow)) + return this.IsCraftActionUnlocked(craftActionRow); + + if (rowRef.TryGetValue(out var emoteRow)) + return this.IsEmoteUnlocked(emoteRow); + + if (rowRef.TryGetValue(out var generalActionRow)) + return this.IsGeneralActionUnlocked(generalActionRow); + + if (rowRef.TryGetValue(out var glassesRow)) + return this.IsGlassesUnlocked(glassesRow); + + if (rowRef.TryGetValue(out var howToRow)) + return this.IsHowToUnlocked(howToRow); + + if (rowRef.TryGetValue(out var instanceContentRow)) + return this.IsInstanceContentUnlocked(instanceContentRow); + + if (rowRef.TryGetValue(out var itemRow)) + return this.IsItemUnlocked(itemRow); + + if (rowRef.TryGetValue(out var mjiLandmarkRow)) + return this.IsMJILandmarkUnlocked(mjiLandmarkRow); + + if (rowRef.TryGetValue(out var mkdLoreRow)) + return this.IsMKDLoreUnlocked(mkdLoreRow); + + if (rowRef.TryGetValue(out var mcGuffinRow)) + return this.IsMcGuffinUnlocked(mcGuffinRow); + + if (rowRef.TryGetValue(out var mountRow)) + return this.IsMountUnlocked(mountRow); + + if (rowRef.TryGetValue(out var notebookDivisionRow)) + return this.IsNotebookDivisionUnlocked(notebookDivisionRow); + + if (rowRef.TryGetValue(out var orchestrionRow)) + return this.IsOrchestrionUnlocked(orchestrionRow); + + if (rowRef.TryGetValue(out var ornamentRow)) + return this.IsOrnamentUnlocked(ornamentRow); + + if (rowRef.TryGetValue(out var performRow)) + return this.IsPerformUnlocked(performRow); + + if (rowRef.TryGetValue(out var publicContentRow)) + return this.IsPublicContentUnlocked(publicContentRow); + + 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 traitRow)) + return this.IsTraitUnlocked(traitRow); + + if (rowRef.TryGetValue(out var tripleTriadCardRow)) + return this.IsTripleTriadCardUnlocked(tripleTriadCardRow); + + return false; + } + + /// + public bool IsUnlockLinkUnlocked(ushort unlockLink) + { + if (!this.IsLoaded) + return false; + + if (unlockLink == 0) + return false; + + return UIState.Instance()->IsUnlockLinkUnlocked(unlockLink); + } + + /// + public bool IsUnlockLinkUnlocked(uint unlockLink) + { + if (!this.IsLoaded) + return false; + + if (unlockLink == 0) + return false; + + return UIState.Instance()->IsUnlockLinkUnlockedOrQuestCompleted(unlockLink); + } + + private void OnLogin() + { + this.Update(); + } + + private void OnLogout(int type, int code) + { + this.cachedUnlockedRowIds.Clear(); + } + + private void OnAgentUpdate(AgentUpdateFlag agentUpdateFlag) + { + if (agentUpdateFlag.HasFlag(AgentUpdateFlag.UnlocksUpdate)) + this.Update(); + } + + private void Update() + { + if (!this.IsLoaded) + return; + + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + this.UpdateUnlocksForSheet(); + + // Not implemented: + // - DescriptionPage: quite complex + // - QuestAcceptAdditionCondition: ignored + + // For some other day: + // - FishingSpot + // - Spearfishing + // - Adventure (Sightseeing) + // - MinerFolkloreTome + // - BotanistFolkloreTome + // - FishingFolkloreTome + // - VVD or is that unlocked via quest? + // - VVDNotebookContents? + // - FramersKit (is that just an Item?) + // - ... more? + + // Subrow sheets, which are incompatible with the current Unlock event, since RowRef doesn't carry the SubrowId: + // - 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 + } + + private void UpdateUnlocksForSheet() where T : struct, IExcelRow + { + var unlockedRowIds = this.cachedUnlockedRowIds.GetOrAdd(typeof(T), _ => []); + + foreach (var row in this.dataManager.GetExcelSheet()) + { + if (unlockedRowIds.Contains(row.RowId)) + continue; + + var rowRef = LuminaUtils.CreateRef(row.RowId); + + if (!this.IsRowRefUnlocked(rowRef)) + continue; + + unlockedRowIds.Add(row.RowId); + + Log.Verbose($"Unlock detected: {typeof(T).Name}#{row.RowId}"); + + foreach (var action in Delegate.EnumerateInvocationList(this.Unlock)) + { + try + { + action((RowRef)rowRef); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); + } + } + } + } +} + +/// +/// Plugin-scoped version of a service. +/// +[PluginInterface] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockState +{ + [ServiceManager.ServiceDependency] + private readonly UnlockState unlockStateService = Service.Get(); + + /// + /// Initializes a new instance of the class. + /// + internal UnlockStatePluginScoped() + { + this.unlockStateService.Unlock += this.UnlockForward; + } + + /// + public event IUnlockState.UnlockDelegate? Unlock; + + /// + public bool IsActionUnlocked(ActionSheet row) => this.unlockStateService.IsActionUnlocked(row); + + /// + public bool IsAetherCurrentCompFlgSetUnlocked(AetherCurrentCompFlgSet row) => this.unlockStateService.IsAetherCurrentCompFlgSetUnlocked(row); + + /// + public bool IsAetherCurrentUnlocked(AetherCurrent row) => this.unlockStateService.IsAetherCurrentUnlocked(row); + + /// + public bool IsAozActionUnlocked(AozAction row) => this.unlockStateService.IsAozActionUnlocked(row); + + /// + public bool IsBannerBgUnlocked(BannerBg row) => this.unlockStateService.IsBannerBgUnlocked(row); + + /// + public bool IsBannerConditionUnlocked(BannerCondition row) => this.unlockStateService.IsBannerConditionUnlocked(row); + + /// + public bool IsBannerDecorationUnlocked(BannerDecoration row) => this.unlockStateService.IsBannerDecorationUnlocked(row); + + /// + public bool IsBannerFacialUnlocked(BannerFacial row) => this.unlockStateService.IsBannerFacialUnlocked(row); + + /// + public bool IsBannerFrameUnlocked(BannerFrame row) => this.unlockStateService.IsBannerFrameUnlocked(row); + + /// + public bool IsBannerTimelineUnlocked(BannerTimeline row) => this.unlockStateService.IsBannerTimelineUnlocked(row); + + /// + public bool IsBuddyActionUnlocked(BuddyAction row) => this.unlockStateService.IsBuddyActionUnlocked(row); + + /// + public bool IsBuddyEquipUnlocked(BuddyEquip row) => this.unlockStateService.IsBuddyEquipUnlocked(row); + + /// + public bool IsCharaMakeCustomizeUnlocked(CharaMakeCustomize row) => this.unlockStateService.IsCharaMakeCustomizeUnlocked(row); + + /// + public bool IsChocoboTaxiStandUnlocked(ChocoboTaxiStand row) => this.unlockStateService.IsChocoboTaxiStandUnlocked(row); + + /// + public bool IsCompanionUnlocked(Companion row) => this.unlockStateService.IsCompanionUnlocked(row); + + /// + public bool IsCraftActionUnlocked(CraftAction row) => this.unlockStateService.IsCraftActionUnlocked(row); + + /// + public bool IsCSBonusContentTypeUnlocked(CSBonusContentType row) => this.unlockStateService.IsCSBonusContentTypeUnlocked(row); + + /// + public bool IsEmoteUnlocked(Emote row) => this.unlockStateService.IsEmoteUnlocked(row); + + /// + public bool IsEmjVoiceNpcUnlocked(EmjVoiceNpc row) => this.unlockStateService.IsEmjVoiceNpcUnlocked(row); + + /// + public bool IsEmjCostumeUnlocked(EmjCostume row) => this.unlockStateService.IsEmjCostumeUnlocked(row); + + /// + public bool IsGeneralActionUnlocked(GeneralAction row) => this.unlockStateService.IsGeneralActionUnlocked(row); + + /// + public bool IsGlassesUnlocked(Glasses row) => this.unlockStateService.IsGlassesUnlocked(row); + + /// + public bool IsHowToUnlocked(HowTo row) => this.unlockStateService.IsHowToUnlocked(row); + + /// + public bool IsInstanceContentUnlocked(InstanceContentSheet row) => this.unlockStateService.IsInstanceContentUnlocked(row); + + /// + public bool IsItemUnlockable(Item row) => this.unlockStateService.IsItemUnlockable(row); + + /// + public bool IsItemUnlocked(Item row) => this.unlockStateService.IsItemUnlocked(row); + + /// + public bool IsMcGuffinUnlocked(McGuffin row) => this.unlockStateService.IsMcGuffinUnlocked(row); + + /// + public bool IsMJILandmarkUnlocked(MJILandmark row) => this.unlockStateService.IsMJILandmarkUnlocked(row); + + /// + public bool IsMKDLoreUnlocked(MKDLore row) => this.unlockStateService.IsMKDLoreUnlocked(row); + + /// + public bool IsMountUnlocked(Mount row) => this.unlockStateService.IsMountUnlocked(row); + + /// + public bool IsNotebookDivisionUnlocked(NotebookDivision row) => this.unlockStateService.IsNotebookDivisionUnlocked(row); + + /// + public bool IsOrchestrionUnlocked(Orchestrion row) => this.unlockStateService.IsOrchestrionUnlocked(row); + + /// + public bool IsOrnamentUnlocked(Ornament row) => this.unlockStateService.IsOrnamentUnlocked(row); + + /// + public bool IsPerformUnlocked(Perform row) => this.unlockStateService.IsPerformUnlocked(row); + + /// + public bool IsPublicContentUnlocked(PublicContentSheet row) => this.unlockStateService.IsPublicContentUnlocked(row); + + /// + public bool IsRecipeUnlocked(Recipe row) => this.unlockStateService.IsRecipeUnlocked(row); + + /// + public bool IsRowRefUnlocked(RowRef rowRef) => this.unlockStateService.IsRowRefUnlocked(rowRef); + + /// + public bool IsRowRefUnlocked(RowRef rowRef) where T : struct, IExcelRow => this.unlockStateService.IsRowRefUnlocked(rowRef); + + /// + public bool IsSecretRecipeBookUnlocked(SecretRecipeBook row) => this.unlockStateService.IsSecretRecipeBookUnlocked(row); + + /// + public bool IsTraitUnlocked(Trait row) => this.unlockStateService.IsTraitUnlocked(row); + + /// + public bool IsTripleTriadCardUnlocked(TripleTriadCard row) => this.unlockStateService.IsTripleTriadCardUnlocked(row); + + /// + public bool IsUnlockLinkUnlocked(uint unlockLink) => this.unlockStateService.IsUnlockLinkUnlocked(unlockLink); + + /// + public bool IsUnlockLinkUnlocked(ushort unlockLink) => this.unlockStateService.IsUnlockLinkUnlocked(unlockLink); + + /// + void IInternalDisposableService.DisposeService() + { + this.unlockStateService.Unlock -= this.UnlockForward; + } + + private void UnlockForward(RowRef rowRef) => this.Unlock?.Invoke(rowRef); +} diff --git a/Dalamud/Interface/ImGuiBackend/Delegates/ImGuiBuildUiDelegate.cs b/Dalamud/Interface/ImGuiBackend/Delegates/ImGuiBuildUiDelegate.cs new file mode 100644 index 000000000..6ebab55c6 --- /dev/null +++ b/Dalamud/Interface/ImGuiBackend/Delegates/ImGuiBuildUiDelegate.cs @@ -0,0 +1,4 @@ +namespace Dalamud.Interface.ImGuiBackend.Delegates; + +/// Delegate to be called when ImGui should be used to layout now. +public delegate void ImGuiBuildUiDelegate(); diff --git a/Dalamud/Interface/ImGuiBackend/Delegates/ImGuiNewInputFrameDelegate.cs b/Dalamud/Interface/ImGuiBackend/Delegates/ImGuiNewInputFrameDelegate.cs new file mode 100644 index 000000000..7397d8d7f --- /dev/null +++ b/Dalamud/Interface/ImGuiBackend/Delegates/ImGuiNewInputFrameDelegate.cs @@ -0,0 +1,4 @@ +namespace Dalamud.Interface.ImGuiBackend.Delegates; + +/// Delegate to be called on new input frame. +public delegate void ImGuiNewInputFrameDelegate(); diff --git a/Dalamud/Interface/ImGuiBackend/Delegates/ImGuiNewRenderFrameDelegate.cs b/Dalamud/Interface/ImGuiBackend/Delegates/ImGuiNewRenderFrameDelegate.cs new file mode 100644 index 000000000..4a4b38b71 --- /dev/null +++ b/Dalamud/Interface/ImGuiBackend/Delegates/ImGuiNewRenderFrameDelegate.cs @@ -0,0 +1,4 @@ +namespace Dalamud.Interface.ImGuiBackend.Delegates; + +/// Delegate to be called on new render frame. +public delegate void ImGuiNewRenderFrameDelegate(); diff --git a/Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs b/Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs index ebb8e67e2..ea609828d 100644 --- a/Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs +++ b/Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGuizmo; using Dalamud.Bindings.ImPlot; +using Dalamud.Interface.ImGuiBackend.Delegates; using Dalamud.Interface.ImGuiBackend.Helpers; using Dalamud.Interface.ImGuiBackend.InputHandler; using Dalamud.Interface.ImGuiBackend.Renderers; @@ -93,13 +94,13 @@ internal sealed unsafe class Dx11Win32Backend : IWin32Backend ~Dx11Win32Backend() => this.ReleaseUnmanagedResources(); /// - public event IImGuiBackend.BuildUiDelegate? BuildUi; + public event ImGuiBuildUiDelegate? BuildUi; /// - public event IImGuiBackend.NewInputFrameDelegate? NewInputFrame; + public event ImGuiNewInputFrameDelegate? NewInputFrame; /// - public event IImGuiBackend.NewRenderFrameDelegate? NewRenderFrame; + public event ImGuiNewRenderFrameDelegate? NewRenderFrame; /// public bool UpdateCursor diff --git a/Dalamud/Interface/ImGuiBackend/IImGuiBackend.cs b/Dalamud/Interface/ImGuiBackend/IImGuiBackend.cs index 9248b4b5a..4a41191b5 100644 --- a/Dalamud/Interface/ImGuiBackend/IImGuiBackend.cs +++ b/Dalamud/Interface/ImGuiBackend/IImGuiBackend.cs @@ -1,4 +1,5 @@ -using Dalamud.Interface.ImGuiBackend.InputHandler; +using Dalamud.Interface.ImGuiBackend.Delegates; +using Dalamud.Interface.ImGuiBackend.InputHandler; using Dalamud.Interface.ImGuiBackend.Renderers; namespace Dalamud.Interface.ImGuiBackend; @@ -6,23 +7,14 @@ namespace Dalamud.Interface.ImGuiBackend; /// Backend for ImGui. internal interface IImGuiBackend : IDisposable { - /// Delegate to be called when ImGui should be used to layout now. - public delegate void BuildUiDelegate(); - - /// Delegate to be called on new input frame. - public delegate void NewInputFrameDelegate(); - - /// Delegaet to be called on new render frame. - public delegate void NewRenderFrameDelegate(); - /// User methods invoked every ImGui frame to construct custom UIs. - event BuildUiDelegate? BuildUi; + event ImGuiBuildUiDelegate? BuildUi; /// User methods invoked every ImGui frame on handling inputs. - event NewInputFrameDelegate? NewInputFrame; + event ImGuiNewInputFrameDelegate? NewInputFrame; /// User methods invoked every ImGui frame on handling renders. - event NewRenderFrameDelegate? NewRenderFrame; + event ImGuiNewRenderFrameDelegate? NewRenderFrame; /// Gets or sets a value indicating whether the cursor should be overridden with the ImGui cursor. /// @@ -36,7 +28,7 @@ internal interface IImGuiBackend : IDisposable /// Gets the input handler. IImGuiInputHandler InputHandler { get; } - + /// Gets the renderer. IImGuiRenderer Renderer { get; } @@ -45,7 +37,7 @@ internal interface IImGuiBackend : IDisposable /// Handles stuff before resizing happens. void OnPreResize(); - + /// Handles stuff after resizing happens. /// The new width. /// The new height. diff --git a/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs b/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs index 1842028e2..596df4c67 100644 --- a/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs +++ b/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs @@ -1,19 +1,18 @@ -using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using Dalamud.Bindings.ImGui; +using Dalamud.Memory; using Serilog; using TerraFX.Interop.Windows; -using static Dalamud.Interface.ImGuiBackend.Helpers.ImGuiViewportHelpers; - using static TerraFX.Interop.Windows.Windows; using ERROR = TerraFX.Interop.Windows.ERROR; @@ -245,6 +244,9 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler return default(LRESULT); } + if (!ImGui.IsWindowHovered(ImGuiHoveredFlags.AnyWindow)) + ImGui.ClearWindowFocus(); + break; } @@ -371,6 +373,14 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler case WM.WM_DISPLAYCHANGE: this.viewportHandler.UpdateMonitors(); break; + + case WM.WM_KILLFOCUS when hWndCurrent == this.hWnd: + if (!ImGui.IsAnyMouseDown() && GetCapture() == hWndCurrent) + ReleaseCapture(); + + ImGui.GetIO().WantCaptureMouse = false; + ImGui.ClearWindowFocus(); + break; } return null; @@ -531,7 +541,7 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler // We still want to return MA_NOACTIVATE // https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-mouseactivate - return 0x3; + return MA.MA_NOACTIVATE; case WM.WM_NCHITTEST: // Let mouse pass-through the window. This will allow the backend to set io.MouseHoveredViewport properly (which is OPTIONAL). // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. @@ -539,8 +549,7 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. if (viewport.Flags.HasFlag(ImGuiViewportFlags.NoInputs)) { - // https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest - return -1; + return HTTRANSPARENT; } break; @@ -575,51 +584,50 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler private struct ViewportHandler : IDisposable { - [SuppressMessage("ReSharper", "CollectionNeverQueried.Local", Justification = "Keeping references alive")] - private readonly List delegateReferences = new(); + private static readonly string WindowClassName = typeof(ViewportHandler).FullName!; private Win32InputHandler input; - private nint classNamePtr; private bool wantUpdateMonitors = true; public ViewportHandler(Win32InputHandler input) { this.input = input; - this.classNamePtr = Marshal.StringToHGlobalUni("ImGui Platform"); var pio = ImGui.GetPlatformIO(); - pio.PlatformCreateWindow = this.RegisterFunctionPointer(this.OnCreateWindow); - pio.PlatformDestroyWindow = this.RegisterFunctionPointer(this.OnDestroyWindow); - pio.PlatformShowWindow = this.RegisterFunctionPointer(this.OnShowWindow); - pio.PlatformSetWindowPos = this.RegisterFunctionPointer(this.OnSetWindowPos); - pio.PlatformGetWindowPos = this.RegisterFunctionPointer(this.OnGetWindowPos); - pio.PlatformSetWindowSize = this.RegisterFunctionPointer(this.OnSetWindowSize); - pio.PlatformGetWindowSize = this.RegisterFunctionPointer(this.OnGetWindowSize); - pio.PlatformSetWindowFocus = this.RegisterFunctionPointer(this.OnSetWindowFocus); - pio.PlatformGetWindowFocus = this.RegisterFunctionPointer(this.OnGetWindowFocus); - pio.PlatformGetWindowMinimized = - this.RegisterFunctionPointer(this.OnGetWindowMinimized); - pio.PlatformSetWindowTitle = this.RegisterFunctionPointer(this.OnSetWindowTitle); - pio.PlatformSetWindowAlpha = this.RegisterFunctionPointer(this.OnSetWindowAlpha); - pio.PlatformUpdateWindow = this.RegisterFunctionPointer(this.OnUpdateWindow); + pio.PlatformCreateWindow = (delegate* unmanaged[Cdecl])&OnCreateWindow; + pio.PlatformDestroyWindow = (delegate* unmanaged[Cdecl])&OnDestroyWindow; + pio.PlatformShowWindow = (delegate* unmanaged[Cdecl])&OnShowWindow; + pio.PlatformSetWindowPos = (delegate* unmanaged[Cdecl])&OnSetWindowPos; + pio.PlatformGetWindowPos = (delegate* unmanaged[Cdecl])&OnGetWindowPos; + pio.PlatformSetWindowSize = (delegate* unmanaged[Cdecl])&OnSetWindowSize; + pio.PlatformGetWindowSize = (delegate* unmanaged[Cdecl])&OnGetWindowSize; + pio.PlatformSetWindowFocus = (delegate* unmanaged[Cdecl])&OnSetWindowFocus; + pio.PlatformGetWindowFocus = (delegate* unmanaged[Cdecl])&OnGetWindowFocus; + pio.PlatformGetWindowMinimized = (delegate* unmanaged[Cdecl])&OnGetWindowMinimized; + pio.PlatformSetWindowTitle = (delegate* unmanaged[Cdecl])&OnSetWindowTitle; + pio.PlatformSetWindowAlpha = (delegate* unmanaged[Cdecl])&OnSetWindowAlpha; + pio.PlatformUpdateWindow = (delegate* unmanaged[Cdecl])&OnUpdateWindow; // pio.Platform_SetImeInputPos = this.RegisterFunctionPointer(this.OnSetImeInputPos); // pio.Platform_GetWindowDpiScale = this.RegisterFunctionPointer(this.OnGetWindowDpiScale); // pio.Platform_ChangedViewport = this.RegisterFunctionPointer(this.OnChangedViewport); - var wcex = new WNDCLASSEXW + fixed (char* windowClassNamePtr = WindowClassName) { - cbSize = (uint)sizeof(WNDCLASSEXW), - style = CS.CS_HREDRAW | CS.CS_VREDRAW, - hInstance = GetModuleHandleW(null), - hbrBackground = (HBRUSH)(1 + COLOR.COLOR_BACKGROUND), - lpfnWndProc = (delegate* unmanaged)Marshal - .GetFunctionPointerForDelegate(this.input.wndProcDelegate), - lpszClassName = (ushort*)this.classNamePtr, - }; + var wcex = new WNDCLASSEXW + { + cbSize = (uint)sizeof(WNDCLASSEXW), + style = CS.CS_HREDRAW | CS.CS_VREDRAW, + hInstance = (HINSTANCE)Marshal.GetHINSTANCE(typeof(ViewportHandler).Module), + hbrBackground = (HBRUSH)(1 + COLOR.COLOR_BACKGROUND), + lpfnWndProc = (delegate* unmanaged)Marshal + .GetFunctionPointerForDelegate(this.input.wndProcDelegate), + lpszClassName = (ushort*)windowClassNamePtr, + }; - if (RegisterClassExW(&wcex) == 0) - throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()) ?? new("RegisterClassEx Fail"); + if (RegisterClassExW(&wcex) == 0) + throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()) ?? new("RegisterClassEx Fail"); + } // Register main window handle (which is owned by the main application, not by us) // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. @@ -647,11 +655,11 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler ImGui.GetPlatformIO().Handle->Monitors = default; } - if (this.classNamePtr != 0) + fixed (char* windowClassNamePtr = WindowClassName) { - UnregisterClassW((ushort*)this.classNamePtr, GetModuleHandleW(null)); - Marshal.FreeHGlobal(this.classNamePtr); - this.classNamePtr = 0; + UnregisterClassW( + (ushort*)windowClassNamePtr, + (HINSTANCE)Marshal.GetHINSTANCE(typeof(ViewportHandler).Module)); } pio.PlatformCreateWindow = null; @@ -740,13 +748,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler } } - private void* RegisterFunctionPointer(T obj) - { - this.delegateReferences.Add(obj); - return Marshal.GetFunctionPointerForDelegate(obj).ToPointer(); - } - - private void OnCreateWindow(ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnCreateWindow(ImGuiViewportPtr viewport) { var data = (ImGuiViewportDataWin32*)Marshal.AllocHGlobal(Marshal.SizeOf()); viewport.PlatformUserData = data; @@ -774,12 +777,12 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler }; AdjustWindowRectEx(&rect, (uint)data->DwStyle, false, (uint)data->DwExStyle); - fixed (char* pwszWindowTitle = "Untitled") + fixed (char* windowClassNamePtr = WindowClassName) { data->Hwnd = CreateWindowExW( (uint)data->DwExStyle, - (ushort*)this.classNamePtr, - (ushort*)pwszWindowTitle, + (ushort*)windowClassNamePtr, + (ushort*)windowClassNamePtr, (uint)data->DwStyle, rect.left, rect.top, @@ -787,8 +790,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler rect.bottom - rect.top, parentWindow, default, - GetModuleHandleW(null), - default); + (HINSTANCE)Marshal.GetHINSTANCE(typeof(ViewportHandler).Module), + null); } data->HwndOwned = true; @@ -796,7 +799,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler viewport.PlatformHandle = viewport.PlatformHandleRaw = data->Hwnd; } - private void OnDestroyWindow(ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnDestroyWindow(ImGuiViewportPtr viewport) { // This is also called on the main viewport for some reason, and we never set that viewport's PlatformUserData if (viewport.PlatformUserData == null) return; @@ -807,7 +811,11 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler { // Transfer capture so if we started dragging from a window that later disappears, we'll still receive the MOUSEUP event. ReleaseCapture(); - SetCapture(this.input.hWnd); + if (viewport.ParentViewportId != 0) + { + var parentViewport = ImGui.FindViewportByID(viewport.ParentViewportId); + SetCapture((HWND)parentViewport.PlatformHandle); + } } if (data->Hwnd != nint.Zero && data->HwndOwned) @@ -826,7 +834,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler viewport.PlatformUserData = viewport.PlatformHandle = null; } - private void OnShowWindow(ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnShowWindow(ImGuiViewportPtr viewport) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; @@ -836,7 +845,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler ShowWindow(data->Hwnd, SW.SW_SHOW); } - private void OnUpdateWindow(ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnUpdateWindow(ImGuiViewportPtr viewport) { // (Optional) Update Win32 style if it changed _after_ creation. // Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful. @@ -897,17 +907,18 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler } } - private Vector2* OnGetWindowPos(Vector2* returnStorage, ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static Vector2* OnGetWindowPos(Vector2* returnValueStorage, ImGuiViewportPtr viewport) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var pt = new POINT { x = 0, y = 0 }; ClientToScreen(data->Hwnd, &pt); - returnStorage->X = pt.x; - returnStorage->Y = pt.y; - return returnStorage; + *returnValueStorage = new(pt.x, pt.y); + return returnValueStorage; } - private void OnSetWindowPos(ImGuiViewportPtr viewport, Vector2 pos) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnSetWindowPos(ImGuiViewportPtr viewport, Vector2 pos) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var rect = new RECT((int)pos.X, (int)pos.Y, (int)pos.X, (int)pos.Y); @@ -924,17 +935,18 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler SWP.SWP_NOACTIVATE); } - private Vector2* OnGetWindowSize(Vector2* returnStorage, ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static Vector2* OnGetWindowSize(Vector2* returnValueStorage, ImGuiViewportPtr viewport) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; RECT rect; GetClientRect(data->Hwnd, &rect); - returnStorage->X = rect.right - rect.left; - returnStorage->Y = rect.bottom - rect.top; - return returnStorage; + *returnValueStorage = new(rect.right - rect.left, rect.bottom - rect.top); + return returnValueStorage; } - private void OnSetWindowSize(ImGuiViewportPtr viewport, Vector2 size) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnSetWindowSize(ImGuiViewportPtr viewport, Vector2 size) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; @@ -952,7 +964,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler SWP.SWP_NOACTIVATE); } - private void OnSetWindowFocus(ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnSetWindowFocus(ImGuiViewportPtr viewport) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; @@ -961,26 +974,30 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler SetFocus(data->Hwnd); } - private bool OnGetWindowFocus(ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static byte OnGetWindowFocus(ImGuiViewportPtr viewport) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; - return GetForegroundWindow() == data->Hwnd; + return GetForegroundWindow() == data->Hwnd ? (byte)1 : (byte)0; } - private bool OnGetWindowMinimized(ImGuiViewportPtr viewport) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static byte OnGetWindowMinimized(ImGuiViewportPtr viewport) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; - return IsIconic(data->Hwnd); + return IsIconic(data->Hwnd) ? (byte)1 : (byte)0; } - private void OnSetWindowTitle(ImGuiViewportPtr viewport, string title) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnSetWindowTitle(ImGuiViewportPtr viewport, byte* title) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; - fixed (char* pwszTitle = title) + fixed (char* pwszTitle = MemoryHelper.ReadStringNullTerminated((nint)title)) SetWindowTextW(data->Hwnd, (ushort*)pwszTitle); } - private void OnSetWindowAlpha(ImGuiViewportPtr viewport, float alpha) + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static void OnSetWindowAlpha(ImGuiViewportPtr viewport, float alpha) { var data = (ImGuiViewportDataWin32*)viewport.PlatformUserData; var style = GetWindowLongW(data->Hwnd, GWL.GWL_EXSTYLE); diff --git a/Dalamud/Interface/ImGuiBackend/Renderers/Dx11Renderer.cs b/Dalamud/Interface/ImGuiBackend/Renderers/Dx11Renderer.cs index 9e97a11b8..2eb4e4970 100644 --- a/Dalamud/Interface/ImGuiBackend/Renderers/Dx11Renderer.cs +++ b/Dalamud/Interface/ImGuiBackend/Renderers/Dx11Renderer.cs @@ -223,15 +223,15 @@ internal unsafe partial class Dx11Renderer : IImGuiRenderer ImDrawDataPtr drawData, bool clearRenderTarget) { + // Do nothing when there's nothing to draw + if (drawData.IsNull || !drawData.Valid) + return; + // Avoid rendering when minimized if (drawData.DisplaySize.X <= 0 || drawData.DisplaySize.Y <= 0) return; - using var oldState = new D3D11DeviceContextStateBackup(this.featureLevel, this.context.Get()); - - // Setup desired DX state - this.SetupRenderState(drawData); - + // Set up render target this.context.Get()->OMSetRenderTargets(1, &renderTargetView, null); if (clearRenderTarget) { @@ -239,17 +239,17 @@ internal unsafe partial class Dx11Renderer : IImGuiRenderer this.context.Get()->ClearRenderTargetView(renderTargetView, (float*)&color); } - if (!drawData.Valid || drawData.CmdListsCount == 0) - return; - + // Stop if there's nothing to draw var cmdLists = new Span(drawData.Handle->CmdLists, drawData.Handle->CmdListsCount); + if (cmdLists.IsEmpty) + return; // Create and grow vertex/index buffers if needed if (this.vertexBufferSize < drawData.TotalVtxCount) this.vertexBuffer.Dispose(); if (this.vertexBuffer.Get() is null) { - this.vertexBufferSize = drawData.TotalVtxCount + 5000; + this.vertexBufferSize = drawData.TotalVtxCount + 8192; var desc = new D3D11_BUFFER_DESC( (uint)(sizeof(ImDrawVert) * this.vertexBufferSize), (uint)D3D11_BIND_FLAG.D3D11_BIND_VERTEX_BUFFER, @@ -264,7 +264,7 @@ internal unsafe partial class Dx11Renderer : IImGuiRenderer this.indexBuffer.Dispose(); if (this.indexBuffer.Get() is null) { - this.indexBufferSize = drawData.TotalIdxCount + 5000; + this.indexBufferSize = drawData.TotalIdxCount + 16384; var desc = new D3D11_BUFFER_DESC( (uint)(sizeof(ushort) * this.indexBufferSize), (uint)D3D11_BIND_FLAG.D3D11_BIND_INDEX_BUFFER, @@ -275,9 +275,14 @@ internal unsafe partial class Dx11Renderer : IImGuiRenderer this.indexBuffer.Attach(buffer); } - // Upload vertex/index data into a single contiguous GPU buffer + using var oldState = new D3D11DeviceContextStateBackup(this.featureLevel, this.context.Get()); + + // Setup desired DX state + this.SetupRenderState(drawData); + try { + // Upload vertex/index data into a single contiguous GPU buffer. var vertexData = default(D3D11_MAPPED_SUBRESOURCE); var indexData = default(D3D11_MAPPED_SUBRESOURCE); this.context.Get()->Map( @@ -306,26 +311,18 @@ internal unsafe partial class Dx11Renderer : IImGuiRenderer targetVertices = targetVertices[vertices.Length..]; targetIndices = targetIndices[indices.Length..]; } - } - finally - { - this.context.Get()->Unmap((ID3D11Resource*)this.vertexBuffer.Get(), 0); - this.context.Get()->Unmap((ID3D11Resource*)this.indexBuffer.Get(), 0); - } - // Setup orthographic projection matrix into our constant buffer. - // Our visible imgui space lies from DisplayPos (LT) to DisplayPos+DisplaySize (RB). - // DisplayPos is (0,0) for single viewport apps. - try - { - var data = default(D3D11_MAPPED_SUBRESOURCE); + // Setup orthographic projection matrix into our constant buffer. + // Our visible imgui space lies from DisplayPos (LT) to DisplayPos+DisplaySize (RB). + // DisplayPos is (0,0) for single viewport apps. + var constantBufferData = default(D3D11_MAPPED_SUBRESOURCE); this.context.Get()->Map( (ID3D11Resource*)this.vertexConstantBuffer.Get(), 0, D3D11_MAP.D3D11_MAP_WRITE_DISCARD, 0, - &data).ThrowOnError(); - *(Matrix4x4*)data.pData = Matrix4x4.CreateOrthographicOffCenter( + &constantBufferData).ThrowOnError(); + *(Matrix4x4*)constantBufferData.pData = Matrix4x4.CreateOrthographicOffCenter( drawData.DisplayPos.X, drawData.DisplayPos.X + drawData.DisplaySize.X, drawData.DisplayPos.Y + drawData.DisplaySize.Y, @@ -335,6 +332,8 @@ internal unsafe partial class Dx11Renderer : IImGuiRenderer } finally { + this.context.Get()->Unmap((ID3D11Resource*)this.vertexBuffer.Get(), 0); + this.context.Get()->Unmap((ID3D11Resource*)this.indexBuffer.Get(), 0); this.context.Get()->Unmap((ID3D11Resource*)this.vertexConstantBuffer.Get(), 0); } @@ -343,8 +342,6 @@ internal unsafe partial class Dx11Renderer : IImGuiRenderer var vertexOffset = 0; var indexOffset = 0; var clipOff = new Vector4(drawData.DisplayPos, drawData.DisplayPos.X, drawData.DisplayPos.Y); - this.context.Get()->PSSetShader(this.pixelShader, null, 0); - this.context.Get()->PSSetSamplers(0, 1, this.sampler.GetAddressOf()); foreach (ref var cmdList in cmdLists) { var cmds = new ImVectorWrapper(cmdList.Handle->CmdBuffer.ToUntyped()); @@ -383,8 +380,8 @@ internal unsafe partial class Dx11Renderer : IImGuiRenderer default: { // User callback, registered via ImDrawList::AddCallback() - var cb = (delegate*)cmd.UserCallback; - cb(cmdList, ref cmd); + var cb = (delegate* unmanaged)cmd.UserCallback; + cb(cmdList, (ImDrawCmdPtr)Unsafe.AsPointer(ref cmd)); break; } } @@ -467,7 +464,8 @@ internal unsafe partial class Dx11Renderer : IImGuiRenderer buffer = this.vertexConstantBuffer.Get(); ctx->VSSetConstantBuffers(0, 1, &buffer); - // PS handled later + ctx->PSSetShader(this.pixelShader, null, 0); + ctx->PSSetSamplers(0, 1, this.sampler.GetAddressOf()); ctx->GSSetShader(null, null, 0); ctx->HSSetShader(null, null, 0); diff --git a/Dalamud/Interface/Internal/Asserts/AssertHandler.cs b/Dalamud/Interface/Internal/Asserts/AssertHandler.cs index 91323f8ac..276dddb57 100644 --- a/Dalamud/Interface/Internal/Asserts/AssertHandler.cs +++ b/Dalamud/Interface/Internal/Asserts/AssertHandler.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; +using Dalamud.Plugin.Internal; using Dalamud.Utility; using Serilog; @@ -55,7 +57,8 @@ internal class AssertHandler : IDisposable /// public unsafe void Setup() { - CustomNativeFunctions.igCustom_SetAssertCallback(Marshal.GetFunctionPointerForDelegate(this.callback).ToPointer()); + CustomNativeFunctions.igCustom_SetAssertCallback( + Marshal.GetFunctionPointerForDelegate(this.callback).ToPointer()); } /// @@ -72,16 +75,52 @@ internal class AssertHandler : IDisposable this.Shutdown(); } + private static string? ExtractImguiFunction(StackTrace stackTrace) + { + var frame = stackTrace.GetFrames() + .FirstOrDefault(f => f.GetMethod()?.DeclaringType?.Namespace == "Dalamud.Bindings.ImGui"); + if (frame == null) + return null; + + var method = frame.GetMethod(); + if (method == null) + return null; + + return $"{method.Name}({string.Join(", ", method.GetParameters().Select(p => p.Name))})"; + } + + private static StackTrace GenerateStackTrace() + { + var trace = DiagnosticUtil.GetUsefulTrace(new StackTrace(true)); + var frames = trace.GetFrames().ToList(); + + // Remove everything that happens in the assert context. + var lastAssertIdx = frames.FindLastIndex(f => f.GetMethod()?.DeclaringType == typeof(AssertHandler)); + if (lastAssertIdx >= 0) + { + frames.RemoveRange(0, lastAssertIdx + 1); + } + + var firstInterfaceManagerIdx = frames.FindIndex(f => f.GetMethod()?.DeclaringType == typeof(InterfaceManager)); + if (firstInterfaceManagerIdx >= 0) + { + frames.RemoveRange(firstInterfaceManagerIdx, frames.Count - firstInterfaceManagerIdx); + } + + return new StackTrace(frames); + } + private unsafe void OnImGuiAssert(void* pExpr, void* pFile, int line) { var expr = Marshal.PtrToStringAnsi(new IntPtr(pExpr)); var file = Marshal.PtrToStringAnsi(new IntPtr(pFile)); if (expr == null || file == null) { - Log.Warning("ImGui assertion failed: {Expr} at {File}:{Line} (failed to parse)", - expr, - file, - line); + Log.Warning( + "ImGui assertion failed: {Expr} at {File}:{Line} (failed to parse)", + expr, + file, + line); return; } @@ -93,7 +132,7 @@ internal class AssertHandler : IDisposable if (!this.ShowAsserts && !this.everShownAssertThisSession) return; - Lazy stackTrace = new(() => DiagnosticUtil.GetUsefulTrace(new StackTrace()).ToString()); + Lazy stackTrace = new(GenerateStackTrace); if (!this.EnableVerboseLogging) { @@ -103,11 +142,12 @@ internal class AssertHandler : IDisposable if (count <= HideThreshold || count % HidePrintEvery == 0) { - Log.Warning("ImGui assertion failed: {Expr} at {File}:{Line} (repeated {Count} times)", - expr, - file, - line, - count); + Log.Warning( + "ImGui assertion failed: {Expr} at {File}:{Line} (repeated {Count} times)", + expr, + file, + line, + count); } } else @@ -117,11 +157,12 @@ internal class AssertHandler : IDisposable } else { - Log.Warning("ImGui assertion failed: {Expr} at {File}:{Line}\n{StackTrace:l}", - expr, - file, - line, - stackTrace.Value); + Log.Warning( + "ImGui assertion failed: {Expr} at {File}:{Line}\n{StackTrace:l}", + expr, + file, + line, + stackTrace.Value.ToString()); } if (!this.ShowAsserts) @@ -145,7 +186,8 @@ internal class AssertHandler : IDisposable } // grab the stack trace now that we've decided to show UI. - _ = stackTrace.Value; + var responsiblePlugin = Service.GetNullable()?.FindCallingPlugin(stackTrace.Value); + var responsibleMethodCall = ExtractImguiFunction(stackTrace.Value); var gitHubUrl = GetRepoUrl(); var showOnGitHubButton = new TaskDialogButton @@ -175,12 +217,37 @@ internal class AssertHandler : IDisposable var ignoreButton = TaskDialogButton.Ignore; TaskDialogButton? result = null; + void DialogThreadStart() { // TODO(goat): This is probably not gonna work if we showed the loading dialog // this session since it already loaded visual styles... Application.EnableVisualStyles(); + string text; + if (responsiblePlugin != null) + { + text = $"The plugin \"{responsiblePlugin.Name}\" appears to have caused an ImGui assertion failure. " + + $"Please report this problem to the plugin's developer.\n\n"; + } + else + { + text = "Some code in a plugin or Dalamud itself has caused an ImGui assertion failure. " + + "Please report this problem in the Dalamud discord.\n\n"; + } + + text += $"You may attempt to continue running the game, but Dalamud UI elements may not work " + + $"correctly, or the game may crash after resuming.\n\n"; + + if (responsibleMethodCall != null) + { + text += $"Assertion failed: {expr} when performing {responsibleMethodCall}\n{file}:{line}"; + } + else + { + text += $"Assertion failed: {expr}\nAt: {file}:{line}"; + } + var page = new TaskDialogPage { Heading = "ImGui assertion failed", @@ -189,9 +256,9 @@ internal class AssertHandler : IDisposable { CollapsedButtonText = "Show stack trace", ExpandedButtonText = "Hide stack trace", - Text = stackTrace.Value, + Text = stackTrace.Value.ToString(), }, - Text = $"Some code in a plugin or Dalamud itself has caused an internal assertion in ImGui to fail. The game will most likely crash now.\n\n{expr}\nAt: {file}:{line}", + Text = text, Icon = TaskDialogIcon.Warning, Buttons = [ diff --git a/Dalamud/Interface/Internal/DalamudIme.cs b/Dalamud/Interface/Internal/DalamudIme.cs index 99f769f79..cdb976333 100644 --- a/Dalamud/Interface/Internal/DalamudIme.cs +++ b/Dalamud/Interface/Internal/DalamudIme.cs @@ -2,11 +2,9 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Numerics; using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Text.Unicode; @@ -46,24 +44,24 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService .ToDictionary(x => x.Item1, x => x.Name); private static readonly UnicodeRange[] HanRange = - { + [ UnicodeRanges.CjkRadicalsSupplement, UnicodeRanges.CjkSymbolsandPunctuation, UnicodeRanges.CjkUnifiedIdeographsExtensionA, UnicodeRanges.CjkUnifiedIdeographs, UnicodeRanges.CjkCompatibilityIdeographs, - UnicodeRanges.CjkCompatibilityForms, + UnicodeRanges.CjkCompatibilityForms // No more; Extension B~ are outside BMP range - }; + ]; private static readonly UnicodeRange[] HangulRange = - { + [ UnicodeRanges.HangulJamo, UnicodeRanges.HangulSyllables, UnicodeRanges.HangulCompatibilityJamo, UnicodeRanges.HangulJamoExtendedA, - UnicodeRanges.HangulJamoExtendedB, - }; + UnicodeRanges.HangulJamoExtendedB + ]; [ServiceManager.ServiceDependency] private readonly DalamudConfiguration dalamudConfiguration = Service.Get(); @@ -109,24 +107,6 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService private bool updateInputLanguage = true; private bool updateImeStatusAgain; - [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1003:Symbols should be spaced correctly", Justification = ".")] - static DalamudIme() - { - nint cimgui; - try - { - _ = ImGui.GetCurrentContext(); - - cimgui = Process.GetCurrentProcess().Modules.Cast() - .First(x => x.ModuleName == "cimgui.dll") - .BaseAddress; - } - catch - { - return; - } - } - [ServiceManager.ServiceConstructor] private DalamudIme(InterfaceManager.InterfaceManagerWithScene imws) { @@ -170,11 +150,11 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService if (!ImGui.GetIO().ConfigInputTextCursorBlink) return true; var textState = GetInputTextState(); - if (textState->Id == 0 || (textState->Flags & ImGuiInputTextFlags.ReadOnly) != 0) + if (textState.ID == 0 || (textState.Flags & ImGuiInputTextFlags.ReadOnly) != 0) return true; - if (textState->CursorAnim <= 0) + if (textState.CursorAnim <= 0) return true; - return textState->CursorAnim % 1.2f <= 0.8f; + return textState.CursorAnim % 1.2f <= 0.8f; } } @@ -227,11 +207,7 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService } } - private static ImGuiInputTextStateWrapper* GetInputTextState() - { - var ctx = ImGui.GetCurrentContext(); - return (ImGuiInputTextStateWrapper*)&ctx.Handle->InputTextState; - } + private static ImGuiInputTextStatePtr GetInputTextState() => new(&ImGui.GetCurrentContext().Handle->InputTextState); private static (string String, bool Supported) ToUcs2(char* data, int nc = -1) { @@ -332,7 +308,7 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService try { var textState = GetInputTextState(); - var invalidTarget = textState->Id == 0 || (textState->Flags & ImGuiInputTextFlags.ReadOnly) != 0; + var invalidTarget = textState.ID == 0 || (textState.Flags & ImGuiInputTextFlags.ReadOnly) != 0; #if IMEDEBUG switch (args.Message) @@ -564,17 +540,17 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService var textState = GetInputTextState(); if (this.temporaryUndoSelection is not null) { - textState->Undo(); - textState->SelectionTuple = this.temporaryUndoSelection.Value; + textState.Undo(); + textState.SetSelectionTuple(this.temporaryUndoSelection.Value); this.temporaryUndoSelection = null; } - textState->SanitizeSelectionRange(); - if (textState->ReplaceSelectionAndPushUndo(newString)) - this.temporaryUndoSelection = textState->SelectionTuple; + textState.SanitizeSelectionRange(); + if (textState.ReplaceSelectionAndPushUndo(newString)) + this.temporaryUndoSelection = textState.GetSelectionTuple(); // Put the cursor at the beginning, so that the candidate window appears aligned with the text. - textState->SetSelectionRange(textState->SelectionTuple.Start, newString.Length, 0); + textState.SetSelectionRange(textState.GetSelectionTuple().Start, newString.Length, 0); if (finalCommit) { @@ -621,7 +597,7 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService this.temporaryUndoSelection = null; var textState = GetInputTextState(); - textState->Stb.SelectStart = textState->Stb.Cursor = textState->Stb.SelectEnd; + textState.Stb.SelectStart = textState.Stb.Cursor = textState.Stb.SelectEnd; this.candidateStrings.Clear(); this.immCandNative = default; @@ -931,185 +907,6 @@ internal sealed unsafe class DalamudIme : IInternalDisposableService } } - /// - /// Ported from imstb_textedit.h. - /// - [StructLayout(LayoutKind.Sequential, Size = 0xE2C)] - private struct StbTextEditState - { - /// - /// Position of the text cursor within the string. - /// - public int Cursor; - - /// - /// Selection start point. - /// - public int SelectStart; - - /// - /// selection start and end point in characters; if equal, no selection. - /// - /// - /// Note that start may be less than or greater than end (e.g. when dragging the mouse, - /// start is where the initial click was, and you can drag in either direction.) - /// - public int SelectEnd; - - /// - /// Each text field keeps its own insert mode state. - /// To keep an app-wide insert mode, copy this value in/out of the app state. - /// - public byte InsertMode; - - /// - /// Page size in number of row. - /// This value MUST be set to >0 for pageup or pagedown in multilines documents. - /// - public int RowCountPerPage; - - // Remainder is stb-private data. - } - - [StructLayout(LayoutKind.Sequential)] - private struct ImGuiInputTextStateWrapper - { - public uint Id; - public int CurLenW; - public int CurLenA; - public ImVector TextWRaw; - public ImVector TextARaw; - public ImVector InitialTextARaw; - public bool TextAIsValid; - public int BufCapacityA; - public float ScrollX; - public StbTextEditState Stb; - public float CursorAnim; - public bool CursorFollow; - public bool SelectedAllMouseLock; - public bool Edited; - public ImGuiInputTextFlags Flags; - - public ImVectorWrapper TextW => new((ImVector*)&this.ThisWrapperPtr->TextWRaw); - - public (int Start, int End, int Cursor) SelectionTuple - { - get => (this.Stb.SelectStart, this.Stb.SelectEnd, this.Stb.Cursor); - set => (this.Stb.SelectStart, this.Stb.SelectEnd, this.Stb.Cursor) = value; - } - - private ImGuiInputTextStateWrapper* ThisWrapperPtr => (ImGuiInputTextStateWrapper*)Unsafe.AsPointer(ref this); - - private ImGuiInputTextState* ThisPtr => (ImGuiInputTextState*)Unsafe.AsPointer(ref this); - - public void SetSelectionRange(int offset, int length, int relativeCursorOffset) - { - this.Stb.SelectStart = offset; - this.Stb.SelectEnd = offset + length; - if (relativeCursorOffset >= 0) - this.Stb.Cursor = this.Stb.SelectStart + relativeCursorOffset; - else - this.Stb.Cursor = this.Stb.SelectEnd + 1 + relativeCursorOffset; - this.SanitizeSelectionRange(); - } - - public void SanitizeSelectionRange() - { - ref var s = ref this.Stb.SelectStart; - ref var e = ref this.Stb.SelectEnd; - ref var c = ref this.Stb.Cursor; - s = Math.Clamp(s, 0, this.CurLenW); - e = Math.Clamp(e, 0, this.CurLenW); - c = Math.Clamp(c, 0, this.CurLenW); - if (s == e) - s = e = c; - if (s > e) - (s, e) = (e, s); - } - - public void Undo() => ImGuiP.Custom_StbTextUndo(this.ThisPtr); - - public bool MakeUndoReplace(int offset, int oldLength, int newLength) - { - if (oldLength == 0 && newLength == 0) - return false; - - ImGuiP.Custom_StbTextMakeUndoReplace(this.ThisPtr, offset, oldLength, newLength); - return true; - } - - public bool ReplaceSelectionAndPushUndo(ReadOnlySpan newText) - { - var off = this.Stb.SelectStart; - var len = this.Stb.SelectEnd - this.Stb.SelectStart; - return this.MakeUndoReplace(off, len, newText.Length) && this.ReplaceChars(off, len, newText); - } - - public bool ReplaceChars(int pos, int len, ReadOnlySpan newText) - { - this.DeleteChars(pos, len); - return this.InsertChars(pos, newText); - } - - // See imgui_widgets.cpp: STB_TEXTEDIT_DELETECHARS - public void DeleteChars(int pos, int n) - { - if (n == 0) - return; - - var dst = this.TextW.Data + pos; - - // We maintain our buffer length in both UTF-8 and wchar formats - this.Edited = true; - this.CurLenA -= Encoding.UTF8.GetByteCount(dst, n); - this.CurLenW -= n; - - // Offset remaining text (FIXME-OPT: Use memmove) - var src = this.TextW.Data + pos + n; - int i; - for (i = 0; src[i] != 0; i++) - dst[i] = src[i]; - dst[i] = '\0'; - } - - // See imgui_widgets.cpp: STB_TEXTEDIT_INSERTCHARS - public bool InsertChars(int pos, ReadOnlySpan newText) - { - if (newText.Length == 0) - return true; - - var isResizable = (this.Flags & ImGuiInputTextFlags.CallbackResize) != 0; - var textLen = this.CurLenW; - Debug.Assert(pos <= textLen, "pos <= text_len"); - - var newTextLenUtf8 = Encoding.UTF8.GetByteCount(newText); - if (!isResizable && newTextLenUtf8 + this.CurLenA + 1 > this.BufCapacityA) - return false; - - // Grow internal buffer if needed - if (newText.Length + textLen + 1 > this.TextW.Length) - { - if (!isResizable) - return false; - - Debug.Assert(textLen < this.TextW.Length, "text_len < this.TextW.Length"); - this.TextW.Resize(textLen + Math.Clamp(newText.Length * 4, 32, Math.Max(256, newText.Length)) + 1); - } - - var text = this.TextW.DataSpan; - if (pos != textLen) - text.Slice(pos, textLen - pos).CopyTo(text[(pos + newText.Length)..]); - newText.CopyTo(text[pos..]); - - this.Edited = true; - this.CurLenW += newText.Length; - this.CurLenA += newTextLenUtf8; - this.TextW[this.CurLenW] = '\0'; - - return true; - } - } - #if IMEDEBUG private static class ImeDebug { diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs index 7afe7e709..af78c5b0c 100644 --- a/Dalamud/Interface/Internal/DalamudInterface.cs +++ b/Dalamud/Interface/Internal/DalamudInterface.cs @@ -33,6 +33,7 @@ using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; using Dalamud.Logging.Internal; using Dalamud.Plugin.Internal; +using Dalamud.Plugin.SelfTest.Internal; using Dalamud.Storage.Assets; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.System.Framework; @@ -103,7 +104,8 @@ internal class DalamudInterface : IInternalDisposableService TitleScreenMenu titleScreenMenu, GameGui gameGui, ConsoleManager consoleManager, - AddonLifecycle addonLifecycle) + AddonLifecycle addonLifecycle, + SelfTestRegistry selfTestRegistry) { this.dalamud = dalamud; this.configuration = configuration; @@ -119,7 +121,7 @@ internal class DalamudInterface : IInternalDisposableService this.pluginStatWindow = new PluginStatWindow() { IsOpen = false }; this.pluginWindow = new PluginInstallerWindow(pluginImageCache, configuration) { IsOpen = false }; this.settingsWindow = new SettingsWindow() { IsOpen = false }; - this.selfTestWindow = new SelfTestWindow() { IsOpen = false }; + this.selfTestWindow = new SelfTestWindow(selfTestRegistry) { IsOpen = false }; this.styleEditorWindow = new StyleEditorWindow() { IsOpen = false }; this.titleScreenMenuWindow = new TitleScreenMenuWindow( clientState, @@ -180,7 +182,7 @@ internal class DalamudInterface : IInternalDisposableService () => Service.GetNullable()?.ToggleDevMenu(), VirtualKey.SHIFT); - if (!configuration.DalamudBetaKind.IsNullOrEmpty()) + if (Util.GetActiveTrack() != "release") { titleScreenMenu.AddEntryCore( Loc.Localize("TSMDalamudDevMenu", "Developer Menu"), @@ -582,16 +584,6 @@ internal class DalamudInterface : IInternalDisposableService if (this.isCreditsDarkening) this.DrawCreditsDarkeningAnimation(); - - // Release focus of any ImGui window if we click into the game. - var io = ImGui.GetIO(); - if (!io.WantCaptureMouse && (global::Windows.Win32.PInvoke.GetKeyState((int)VirtualKey.LBUTTON) & 0x8000) != 0) - { - unsafe - { - ImGui.SetWindowFocus((byte*)null); - } - } } catch (Exception ex) { @@ -863,9 +855,9 @@ internal class DalamudInterface : IInternalDisposableService this.OpenBranchSwitcher(); } - ImGui.MenuItem(this.dalamud.StartInfo.GameVersion?.ToString() ?? "Unknown version", false); - ImGui.MenuItem($"D: {Util.GetScmVersion()} CS: {Util.GetGitHashClientStructs()}[{FFXIVClientStructs.ThisAssembly.Git.Commits}]", false); - ImGui.MenuItem($"CLR: {Environment.Version}", false); + ImGui.MenuItem(this.dalamud.StartInfo.GameVersion?.ToString() ?? "Unknown version", 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(); } @@ -1028,8 +1020,8 @@ internal class DalamudInterface : IInternalDisposableService } ImGui.Separator(); - ImGui.MenuItem("API Level:" + PluginManager.DalamudApiLevel, false); - ImGui.MenuItem("Loaded plugins:" + pluginManager.InstalledPlugins.Count(), false); + ImGui.MenuItem("API Level:" + PluginManager.DalamudApiLevel, false, false); + ImGui.MenuItem("Loaded plugins:" + pluginManager.InstalledPlugins.Count(), false, false); ImGui.EndMenu(); } @@ -1075,7 +1067,8 @@ internal class DalamudInterface : IInternalDisposableService { ImGui.PushFont(InterfaceManager.MonoFont); - ImGui.BeginMenu(Util.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/ImGuiInputTextStatePtrExtensions.cs b/Dalamud/Interface/Internal/ImGuiInputTextStatePtrExtensions.cs new file mode 100644 index 000000000..ab5fdaac8 --- /dev/null +++ b/Dalamud/Interface/Internal/ImGuiInputTextStatePtrExtensions.cs @@ -0,0 +1,123 @@ +using System.Diagnostics; +using System.Text; + +using Dalamud.Bindings.ImGui; + +namespace Dalamud.Interface.Internal; + +#pragma warning disable SA1600 +internal static unsafe class ImGuiInputTextStatePtrExtensions +{ + public static (int Start, int End, int Cursor) GetSelectionTuple(this ImGuiInputTextStatePtr self) => + (self.Stb.SelectStart, self.Stb.SelectEnd, self.Stb.Cursor); + + public static void SetSelectionTuple(this ImGuiInputTextStatePtr self, (int Start, int End, int Cursor) value) => + (self.Stb.SelectStart, self.Stb.SelectEnd, self.Stb.Cursor) = value; + + public static void SetSelectionRange(this ImGuiInputTextStatePtr self, int offset, int length, int relativeCursorOffset) + { + self.Stb.SelectStart = offset; + self.Stb.SelectEnd = offset + length; + if (relativeCursorOffset >= 0) + self.Stb.Cursor = self.Stb.SelectStart + relativeCursorOffset; + else + self.Stb.Cursor = self.Stb.SelectEnd + 1 + relativeCursorOffset; + self.SanitizeSelectionRange(); + } + + public static void SanitizeSelectionRange(this ImGuiInputTextStatePtr self) + { + ref var s = ref self.Stb.SelectStart; + ref var e = ref self.Stb.SelectEnd; + ref var c = ref self.Stb.Cursor; + s = Math.Clamp(s, 0, self.CurLenW); + e = Math.Clamp(e, 0, self.CurLenW); + c = Math.Clamp(c, 0, self.CurLenW); + if (s == e) + s = e = c; + if (s > e) + (s, e) = (e, s); + } + + public static void Undo(this ImGuiInputTextStatePtr self) => ImGuiP.Custom_StbTextUndo(self); + + public static bool MakeUndoReplace(this ImGuiInputTextStatePtr self, int offset, int oldLength, int newLength) + { + if (oldLength == 0 && newLength == 0) + return false; + + ImGuiP.Custom_StbTextMakeUndoReplace(self, offset, oldLength, newLength); + return true; + } + + public static bool ReplaceSelectionAndPushUndo(this ImGuiInputTextStatePtr self, ReadOnlySpan newText) + { + var off = self.Stb.SelectStart; + var len = self.Stb.SelectEnd - self.Stb.SelectStart; + return self.MakeUndoReplace(off, len, newText.Length) && self.ReplaceChars(off, len, newText); + } + + public static bool ReplaceChars(this ImGuiInputTextStatePtr self, int pos, int len, ReadOnlySpan newText) + { + self.DeleteChars(pos, len); + return self.InsertChars(pos, newText); + } + + // See imgui_widgets.cpp: STB_TEXTEDIT_DELETECHARS + public static void DeleteChars(this ImGuiInputTextStatePtr self, int pos, int n) + { + if (n == 0) + return; + + var dst = (char*)self.TextW.Data + pos; + + // We maintain our buffer length in both UTF-8 and wchar formats + self.Edited = true; + self.CurLenA -= Encoding.UTF8.GetByteCount(dst, n); + self.CurLenW -= n; + + // Offset remaining text (FIXME-OPT: Use memmove) + var src = (char*)self.TextW.Data + pos + n; + int i; + for (i = 0; src[i] != 0; i++) + dst[i] = src[i]; + dst[i] = '\0'; + } + + // See imgui_widgets.cpp: STB_TEXTEDIT_INSERTCHARS + public static bool InsertChars(this ImGuiInputTextStatePtr self, int pos, ReadOnlySpan newText) + { + if (newText.Length == 0) + return true; + + var isResizable = (self.Flags & ImGuiInputTextFlags.CallbackResize) != 0; + var textLen = self.CurLenW; + Debug.Assert(pos <= textLen, "pos <= text_len"); + + var newTextLenUtf8 = Encoding.UTF8.GetByteCount(newText); + if (!isResizable && newTextLenUtf8 + self.CurLenA + 1 > self.BufCapacityA) + return false; + + // Grow internal buffer if needed + if (newText.Length + textLen + 1 > self.TextW.Size) + { + if (!isResizable) + return false; + + Debug.Assert(textLen < self.TextW.Size, "text_len < self.TextW.Length"); + self.TextW.Resize(textLen + Math.Clamp(newText.Length * 4, 32, Math.Max(256, newText.Length)) + 1); + } + + var text = new Span(self.TextW.Data, self.TextW.Size); + if (pos != textLen) + text.Slice(pos, textLen - pos).CopyTo(text[(pos + newText.Length)..]); + newText.CopyTo(text[pos..]); + + self.Edited = true; + self.CurLenW += newText.Length; + self.CurLenA += newTextLenUtf8; + self.TextW[self.CurLenW] = '\0'; + + return true; + } +} diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index c096b428f..76a1b5172 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -18,6 +18,7 @@ using Dalamud.Hooking; using Dalamud.Hooking.Internal; using Dalamud.Hooking.WndProcHook; using Dalamud.Interface.ImGuiBackend; +using Dalamud.Interface.ImGuiBackend.Delegates; using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification.Internal; using Dalamud.Interface.Internal.Asserts; @@ -128,7 +129,7 @@ internal partial class InterfaceManager : IInternalDisposableService /// /// This event gets called each frame to facilitate ImGui drawing. /// - public event IImGuiBackend.BuildUiDelegate? Draw; + public event ImGuiBuildUiDelegate? Draw; /// /// This event gets called when ResizeBuffers is called. @@ -518,15 +519,15 @@ internal partial class InterfaceManager : IInternalDisposableService /// Safely invoke . internal void InvokeGlobalScaleChanged() - => DefaultGlobalScaleChanged.InvokeSafely(); + => this.DefaultGlobalScaleChanged.InvokeSafely(); /// Safely invoke . internal void InvokeFontChanged() - => DefaultFontChanged.InvokeSafely(); + => this.DefaultFontChanged.InvokeSafely(); /// Safely invoke . internal void InvokeStyleChanged() - => DefaultStyleChanged.InvokeSafely(); + => this.DefaultStyleChanged.InvokeSafely(); private static InterfaceManager WhenFontsReady() { @@ -634,29 +635,6 @@ internal partial class InterfaceManager : IInternalDisposableService Service.ProvideException(ex); Log.Error(ex, "Could not load ImGui dependencies."); - fixed (void* lpText = - "Dalamud plugins require the Microsoft Visual C++ Redistributable to be installed.\nPlease install the runtime from the official Microsoft website or disable Dalamud.\n\nDo you want to download the redistributable now?") - { - fixed (void* lpCaption = "Dalamud Error") - { - var res = MessageBoxW( - default, - (ushort*)lpText, - (ushort*)lpCaption, - MB.MB_YESNO | MB.MB_TOPMOST | MB.MB_ICONERROR); - - if (res == IDYES) - { - var psi = new ProcessStartInfo - { - FileName = "https://aka.ms/vs/16/release/vc_redist.x64.exe", - UseShellExecute = true, - }; - Process.Start(psi); - } - } - } - Environment.Exit(-1); // Doesn't reach here, but to make the compiler not complain @@ -1197,6 +1175,7 @@ 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 71c869ede..d3aea7f57 100644 --- a/Dalamud/Interface/Internal/PluginCategoryManager.cs +++ b/Dalamud/Interface/Internal/PluginCategoryManager.cs @@ -294,7 +294,7 @@ internal class PluginCategoryManager } } - if (PluginManager.HasTestingVersion(manifest) || manifest.IsTestingExclusive) + if (manifest.IsTestingExclusive || manifest.IsAvailableForTesting) categoryList.Add(CategoryKind.AvailableForTesting); // always add, even if empty diff --git a/Dalamud/Interface/Internal/UiDebug.cs b/Dalamud/Interface/Internal/UiDebug.cs index a7c73165c..1211b505d 100644 --- a/Dalamud/Interface/Internal/UiDebug.cs +++ b/Dalamud/Interface/Internal/UiDebug.cs @@ -243,8 +243,6 @@ internal unsafe class UiDebug ImGui.Text($"BGColor: #{textNode->BackgroundColor.R:X2}{textNode->BackgroundColor.G:X2}{textNode->BackgroundColor.B:X2}{textNode->BackgroundColor.A:X2}"); ImGui.Text($"TextFlags: {textNode->TextFlags}"); - ImGui.SameLine(); - ImGui.Text($"TextFlags2: {textNode->TextFlags2}"); break; case NodeType.Counter: @@ -416,11 +414,11 @@ internal unsafe class UiDebug var textInputComponent = (AtkComponentTextInput*)compNode->Component; ImGui.Text("InputBase Text1: "u8); ImGui.SameLine(); - Service.Get().Draw(textInputComponent->AtkComponentInputBase.UnkText1); + Service.Get().Draw(textInputComponent->AtkComponentInputBase.EvaluatedString); ImGui.Text("InputBase Text2: "u8); ImGui.SameLine(); - Service.Get().Draw(textInputComponent->AtkComponentInputBase.UnkText2); + Service.Get().Draw(textInputComponent->AtkComponentInputBase.RawString); ImGui.Text("Text1: "u8); ImGui.SameLine(); diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs index b3e7d1b21..a35195498 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs @@ -90,9 +90,9 @@ internal unsafe class ComponentNodeTree : ResNodeTree case TextInput: var textInputComponent = (AtkComponentTextInput*)this.Component; ImGui.Text( - $"InputBase Text1: {Marshal.PtrToStringAnsi(new(textInputComponent->AtkComponentInputBase.UnkText1.StringPtr))}"); + $"InputBase Text1: {Marshal.PtrToStringAnsi(new(textInputComponent->AtkComponentInputBase.EvaluatedString.StringPtr))}"); ImGui.Text( - $"InputBase Text2: {Marshal.PtrToStringAnsi(new(textInputComponent->AtkComponentInputBase.UnkText2.StringPtr))}"); + $"InputBase Text2: {Marshal.PtrToStringAnsi(new(textInputComponent->AtkComponentInputBase.RawString.StringPtr))}"); ImGui.Text( $"Text1: {Marshal.PtrToStringAnsi(new(textInputComponent->UnkText01.StringPtr))}"); ImGui.Text( diff --git a/Dalamud/Interface/Internal/Windows/BranchSwitcherWindow.cs b/Dalamud/Interface/Internal/Windows/BranchSwitcherWindow.cs index 5caf8b5be..9cc14ea14 100644 --- a/Dalamud/Interface/Internal/Windows/BranchSwitcherWindow.cs +++ b/Dalamud/Interface/Internal/Windows/BranchSwitcherWindow.cs @@ -6,13 +6,16 @@ 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; using Dalamud.Networking.Http; +using Dalamud.Utility; + using Newtonsoft.Json; +using Serilog; + namespace Dalamud.Interface.Internal.Windows; /// @@ -44,13 +47,12 @@ public class BranchSwitcherWindow : Window this.branches = await client.GetFromJsonAsync>(BranchInfoUrl); Debug.Assert(this.branches != null, "this.branches != null"); - var config = Service.Get(); - this.selectedBranchIndex = this.branches!.Any(x => x.Key == config.DalamudBetaKind) ? - this.branches.TakeWhile(x => x.Key != config.DalamudBetaKind).Count() - : 0; - - if (this.branches.ElementAt(this.selectedBranchIndex).Value.Key != config.DalamudBetaKey) + var trackName = Util.GetActiveTrack(); + this.selectedBranchIndex = this.branches.IndexOf(x => x.Value.Track == trackName); + if (this.selectedBranchIndex == -1) + { this.selectedBranchIndex = 0; + } }); base.OnOpen(); @@ -83,35 +85,27 @@ public class BranchSwitcherWindow : Window ImGuiHelpers.ScaledDummy(5); - void Pick() - { - var config = Service.Get(); - config.DalamudBetaKind = pickedBranch.Key; - config.DalamudBetaKey = pickedBranch.Value.Key; - config.QueueSave(); - } - - if (ImGui.Button("Pick"u8)) - { - Pick(); - this.IsOpen = false; - } - - ImGui.SameLine(); - if (ImGui.Button("Pick & Restart"u8)) { - Pick(); - - // If we exit immediately, we need to write out the new config now - Service.Get().ForceSave(); + var newTrackName = pickedBranch.Key; + var newTrackKey = pickedBranch.Value.Key; + Log.Verbose("Switching to branch {Branch} with key {Key}", newTrackName, newTrackKey); var appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - var xlPath = Path.Combine(appData, "XIVLauncher", "XIVLauncher.exe"); + var xlPath = Path.Combine(appData, "XIVLauncher", "current", "XIVLauncher.exe"); if (File.Exists(xlPath)) { - Process.Start(xlPath); + var ps = new ProcessStartInfo + { + FileName = xlPath, + UseShellExecute = false, + }; + + ps.ArgumentList.Add($"--dalamud-beta-kind={newTrackName}"); + ps.ArgumentList.Add($"--dalamud-beta-key={(newTrackKey.IsNullOrEmpty() ? "invalid" : newTrackKey)}"); + + Process.Start(ps); Environment.Exit(0); } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonLifecycleWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonLifecycleWidget.cs index 15c27d5b7..b58166e89 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonLifecycleWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonLifecycleWidget.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -117,9 +118,11 @@ public class AddonLifecycleWidget : IDataWindowWidget { ImGui.Columns(2); + var functionAddress = receiveEventListener.FunctionAddress; + ImGui.Text("Hook Address"u8); ImGui.NextColumn(); - ImGui.Text(receiveEventListener.FunctionAddress.ToString("X")); + ImGui.Text($"0x{functionAddress:X} (ffxiv_dx11.exe+{functionAddress - Process.GetCurrentProcess().MainModule!.BaseAddress:X})"); ImGui.NextColumn(); ImGui.Text("Hook Status"u8); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs index e0e3305b3..c3074e807 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs @@ -81,11 +81,10 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget var stringArrayData = (StringArrayData*)arrays[arrayIndex]; for (var rowIndex = 0; rowIndex < arrays[arrayIndex]->Size; rowIndex++) { - var isNull = (nint)stringArrayData->StringArray[rowIndex] == 0; - if (isNull) + if (!stringArrayData->StringArray[rowIndex].HasValue) continue; - if (new ReadOnlySeStringSpan(stringArrayData->StringArray[rowIndex]).ExtractText().Contains(this.searchTerm, StringComparison.InvariantCultureIgnoreCase)) + if (new ReadOnlySeStringSpan(stringArrayData->StringArray[rowIndex].Value).ExtractText().Contains(this.searchTerm, StringComparison.InvariantCultureIgnoreCase)) rowsFound++; } @@ -289,7 +288,7 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget for (var i = 0; i < array->Size; i++) { - var isNull = (nint)array->StringArray[i] == 0; + var isNull = !array->StringArray[i].HasValue; if (isNull && this.hideUnsetStringArrayEntries) continue; @@ -298,7 +297,7 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget if (isNull) continue; - if (!new ReadOnlySeStringSpan(array->StringArray[i]).ExtractText().Contains(this.searchTerm, StringComparison.InvariantCultureIgnoreCase)) + if (!new ReadOnlySeStringSpan(array->StringArray[i].Value).ExtractText().Contains(this.searchTerm, StringComparison.InvariantCultureIgnoreCase)) continue; } @@ -312,7 +311,7 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget if (this.showTextAddress) { if (!isNull) - WidgetUtil.DrawCopyableText($"0x{(nint)array->StringArray[i]:X}", "Copy text address"); + WidgetUtil.DrawCopyableText($"0x{(nint)array->StringArray[i].Value:X}", "Copy text address"); } else { @@ -322,7 +321,7 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget ImGui.TableNextColumn(); // Managed if (!isNull) { - ImGui.Text(((nint)array->StringArray[i] != 0 && array->ManagedStringArray[i] == array->StringArray[i]).ToString()); + ImGui.Text((array->StringArray[i].HasValue && array->ManagedStringArray[i].Value == array->StringArray[i]).ToString()); } ImGui.TableNextColumn(); // Text @@ -330,11 +329,11 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget { if (this.showMacroString) { - WidgetUtil.DrawCopyableText(new ReadOnlySeStringSpan(array->StringArray[i]).ToString(), "Copy text"); + WidgetUtil.DrawCopyableText(new ReadOnlySeStringSpan(array->StringArray[i].Value).ToString(), "Copy text"); } else { - ImGuiHelpers.SeStringWrapped(new ReadOnlySeStringSpan(array->StringArray[i])); + ImGuiHelpers.SeStringWrapped(new ReadOnlySeStringSpan(array->StringArray[i].Value)); } } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs index 07ff2fdce..06dc1b11e 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs @@ -40,7 +40,7 @@ internal class BuddyListWidget : IDataWindowWidget } else { - ImGui.Text($"[Companion] {member.Address.ToInt64():X} - {member.ObjectId} - {member.DataID}"); + ImGui.Text($"[Companion] {member.Address.ToInt64():X} - {member.EntityId} - {member.DataID}"); if (this.resolveGameData) { var gameObject = member.GameObject; @@ -64,7 +64,7 @@ internal class BuddyListWidget : IDataWindowWidget } else { - ImGui.Text($"[Pet] {member.Address.ToInt64():X} - {member.ObjectId} - {member.DataID}"); + ImGui.Text($"[Pet] {member.Address.ToInt64():X} - {member.EntityId} - {member.DataID}"); if (this.resolveGameData) { var gameObject = member.GameObject; @@ -91,7 +91,7 @@ internal class BuddyListWidget : IDataWindowWidget for (var i = 0; i < count; i++) { var member = buddyList[i]; - ImGui.Text($"[BattleBuddy] [{i}] {member?.Address.ToInt64():X} - {member?.ObjectId} - {member?.DataID}"); + ImGui.Text($"[BattleBuddy] [{i}] {member?.Address.ToInt64():X} - {member?.EntityId} - {member?.DataID}"); if (this.resolveGameData) { var gameObject = member?.GameObject; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs index bf6800a53..7a5a9c89b 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs @@ -1,7 +1,7 @@ using Dalamud.Bindings.ImGui; -using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.JobGauge; using Dalamud.Game.ClientState.JobGauge.Types; +using Dalamud.Game.ClientState.Objects; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -29,10 +29,10 @@ internal class GaugeWidget : IDataWindowWidget /// public void Draw() { - var clientState = Service.Get(); + var objectTable = Service.Get(); var jobGauges = Service.Get(); - var player = clientState.LocalPlayer; + var player = objectTable.LocalPlayer; if (player == null) { ImGui.Text("Player is not present"u8); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs index 290c7d9a2..71fb18352 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs @@ -4,6 +4,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.Gui; +using Dalamud.Game.Player; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -39,12 +40,13 @@ internal class ObjectTableWidget : IDataWindowWidget var chatGui = Service.Get(); var clientState = Service.Get(); + var playerState = Service.Get(); var gameGui = Service.Get(); var objectTable = Service.Get(); var stateString = string.Empty; - if (clientState.LocalPlayer == null) + if (objectTable.LocalPlayer == null) { ImGui.Text("LocalPlayer null."u8); } @@ -55,10 +57,10 @@ internal class ObjectTableWidget : IDataWindowWidget else { stateString += $"ObjectTableLen: {objectTable.Length}\n"; - stateString += $"LocalPlayerName: {clientState.LocalPlayer.Name}\n"; - stateString += $"CurrentWorldName: {(this.resolveGameData ? clientState.LocalPlayer.CurrentWorld.ValueNullable?.Name : clientState.LocalPlayer.CurrentWorld.RowId.ToString())}\n"; - stateString += $"HomeWorldName: {(this.resolveGameData ? clientState.LocalPlayer.HomeWorld.ValueNullable?.Name : clientState.LocalPlayer.HomeWorld.RowId.ToString())}\n"; - stateString += $"LocalCID: {clientState.LocalContentId:X}\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"; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs index 6c581604e..0ca754a91 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs @@ -1,10 +1,10 @@ -using Dalamud.Bindings.ImGui; -using Dalamud.Game.ClientState; +using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Ipc.Internal; using Dalamud.Utility; + using Serilog; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -111,12 +111,12 @@ internal class PluginIpcWidget : IDataWindowWidget if (ImGui.Button("Action GO"u8)) { - this.ipcSubGo.InvokeAction(Service.Get().LocalPlayer); + this.ipcSubGo.InvokeAction(Service.Get().LocalPlayer); } if (ImGui.Button("Func GO"u8)) { - this.callGateResponse = this.ipcSubGo.InvokeFunc(Service.Get().LocalPlayer); + this.callGateResponse = this.ipcSubGo.InvokeFunc(Service.Get().LocalPlayer); } if (!this.callGateResponse.IsNullOrEmpty()) diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs index 3d79fd398..a88f576f9 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text; +using System.Threading.Tasks; using Dalamud.Bindings.ImGui; -using Dalamud.Configuration.Internal; using Dalamud.Data; using Dalamud.Game; using Dalamud.Game.ClientState; @@ -13,12 +13,13 @@ using Dalamud.Game.Text.Noun.Enums; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; -using Dalamud.Memory; using Dalamud.Utility; + using FFXIVClientStructs.FFXIV.Client.System.String; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI.Misc; using FFXIVClientStructs.FFXIV.Component.Text; + using Lumina.Data; using Lumina.Data.Files.Excel; using Lumina.Data.Structs.Excel; @@ -89,6 +90,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget { MacroCode.FrNoun, ["SheetName", "ArticleType", "RowId", "Amount", "Case", "UnkInt5"] }, { MacroCode.ChNoun, ["SheetName", "ArticleType", "RowId", "Amount", "Case", "UnkInt5"] }, { MacroCode.LowerHead, ["String"] }, + { MacroCode.SheetSub, ["SheetName", "RowId", "SubrowId", "ColumnIndex", "SecondarySheetName", "SecondarySheetColumnIndex"] }, { MacroCode.ColorType, ["ColorType"] }, { MacroCode.EdgeColorType, ["ColorType"] }, { MacroCode.Ruby, ["StandardText", "RubyText"] }, @@ -103,13 +105,16 @@ internal class SeStringCreatorWidget : IDataWindowWidget { LinkMacroPayloadType.Character, ["Flags", "WorldId"] }, { LinkMacroPayloadType.Item, ["ItemId", "Rarity"] }, { LinkMacroPayloadType.MapPosition, ["TerritoryType/MapId", "RawX", "RawY"] }, - { LinkMacroPayloadType.Quest, ["QuestId"] }, - { LinkMacroPayloadType.Achievement, ["AchievementId"] }, - { LinkMacroPayloadType.HowTo, ["HowToId"] }, + { LinkMacroPayloadType.Quest, ["RowId"] }, + { LinkMacroPayloadType.Achievement, ["RowId"] }, + { LinkMacroPayloadType.HowTo, ["RowId"] }, // PartyFinderNotification { LinkMacroPayloadType.Status, ["StatusId"] }, { LinkMacroPayloadType.PartyFinder, ["ListingId", string.Empty, "WorldId"] }, - { LinkMacroPayloadType.AkatsukiNote, ["AkatsukiNoteId"] }, + { LinkMacroPayloadType.AkatsukiNote, ["RowId"] }, + { LinkMacroPayloadType.Description, ["RowId"] }, + { LinkMacroPayloadType.WKSPioneeringTrail, ["RowId", "SubrowId"] }, + { LinkMacroPayloadType.MKDLore, ["RowId"] }, { DalamudLinkType, ["CommandId", "Extra1", "Extra2", "ExtraString"] }, }; @@ -142,6 +147,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget private SeStringParameter[]? localParameters = [Util.GetScmVersion()]; private ReadOnlySeString input; private ClientLanguage? language; + private Task? validImportSheetNamesTask; private int importSelectedSheetName; private int importRowId; private string[]? validImportSheetNames; @@ -309,13 +315,13 @@ internal class SeStringCreatorWidget : IDataWindowWidget ImGui.Text(i switch { 0 => "Player Name", - 1 => "Temp Player 1 Name", - 2 => "Temp Player 2 Name", + 1 => "Temp Entity 1: Name", + 2 => "Temp Entity 2: Name", 3 => "Player Sex", - 4 => "Temp Player 1 Sex", - 5 => "Temp Player 2 Sex", - 6 => "Temp Player 1 Unk 1", - 7 => "Temp Player 2 Unk 1", + 4 => "Temp Entity 1: Sex", + 5 => "Temp Entity 2: Sex", + 6 => "Temp Entity 1: ObjStrId", + 7 => "Temp Entity 2: ObjStrId", 10 => "Eorzea Time Hours", 11 => "Eorzea Time Minutes", 12 => "ColorSay", @@ -364,14 +370,19 @@ internal class SeStringCreatorWidget : IDataWindowWidget 62 => "ColorLoot", 63 => "ColorCraft", 64 => "ColorGathering", - 65 => "Temp Player 1 Unk 2", - 66 => "Temp Player 2 Unk 2", + 65 => "Temp Entity 1: Name starts with Vowel", + 66 => "Temp Entity 2: Name starts with Vowel", 67 => "Player ClassJobId", 68 => "Player Level", + 69 => "Player StartTown", 70 => "Player Race", 71 => "Player Synced Level", - 77 => "Client/Plattform?", + 73 => "Quest#66047: Has met Alphinaud and Alisaie", + 74 => "PlayStation Generation", + 75 => "Is Legacy Player", + 77 => "Client/Platform?", 78 => "Player BirthMonth", + 79 => "PadMode", 82 => "Datacenter Region", 83 => "ColorCWLS2", 84 => "ColorCWLS3", @@ -392,6 +403,11 @@ internal class SeStringCreatorWidget : IDataWindowWidget 100 => "LogSetRoleColor 1: LogColorOtherClass", 101 => "LogSetRoleColor 2: LogColorOtherClass", 102 => "Has Login Security Token", + 103 => "Is subscribed to PlayStation Plus", + 104 => "PadMouseMode", + 106 => "Preferred World Bonus Max Level", + 107 => "Occult Crescent Support Job Level", + 108 => "Deep Dungeon Id", _ => string.Empty, }); } @@ -506,7 +522,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget } } - ImGui.SetClipboardText(sb.ToReadOnlySeString().ToString()); + ImGui.SetClipboardText(sb.ToReadOnlySeString().ToMacroString()); } ImGui.SameLine(); @@ -551,22 +567,31 @@ internal class SeStringCreatorWidget : IDataWindowWidget var dataManager = Service.Get(); - this.validImportSheetNames ??= dataManager.Excel.SheetNames.Where(sheetName => + this.validImportSheetNamesTask ??= Task.Run(() => { - try + this.validImportSheetNames = dataManager.Excel.SheetNames.Where(sheetName => { - var headerFile = dataManager.GameData.GetFile($"exd/{sheetName}.exh"); - if (headerFile.Header.Variant != ExcelVariant.Default) - return false; + try + { + var headerFile = dataManager.GameData.GetFile($"exd/{sheetName}.exh"); + if (headerFile.Header.Variant != ExcelVariant.Default) + return false; - var sheet = dataManager.Excel.GetSheet(Language.English, sheetName); - return sheet.Columns.Any(col => col.Type == ExcelColumnDataType.String); - } - catch - { - return false; - } - }).OrderBy(sheetName => sheetName, StringComparer.InvariantCulture).ToArray(); + var sheet = dataManager.Excel.GetSheet(Language.English, sheetName); + return sheet.Columns.Any(col => col.Type == ExcelColumnDataType.String); + } + catch + { + return false; + } + }).OrderBy(sheetName => sheetName, StringComparer.InvariantCulture).ToArray(); + }); + + if (this.validImportSheetNames == null) + { + ImGui.Text("Loading sheets..."u8); + return; + } var sheetChanged = ImGui.Combo("Sheet Name", ref this.importSelectedSheetName, this.validImportSheetNames); @@ -621,7 +646,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget ImGui.Text(i.ToString()); ImGui.TableNextColumn(); - if (ImGui.Selectable($"{value.ToString().Truncate(100)}###Column{i}")) + if (ImGui.Selectable($"{value.ToMacroString().Truncate(100)}###Column{i}")) { foreach (var payload in value) { @@ -692,7 +717,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget ImGui.TableNextColumn(); // Text var message = entry.Message; ImGui.SetNextItemWidth(-1); - if (ImGui.InputText($"##{i}_Message", ref message, 255)) + if (ImGui.InputText($"##{i}_Message", ref message, 2048)) { entry.Message = message; updateString |= true; @@ -1065,10 +1090,10 @@ internal class SeStringCreatorWidget : IDataWindowWidget break; case LinkMacroPayloadType.AkatsukiNote when - dataManager.GetSubrowExcelSheet(this.language).TryGetRow(u32, out var akatsukiNoteRow) && - dataManager.GetExcelSheet(this.language).TryGetRow((uint)akatsukiNoteRow[0].Unknown2, out var akatsukiNoteStringRow): + dataManager.GetSubrowExcelSheet(this.language).TryGetSubrow(u32, 0, out var akatsukiNoteRow) && + akatsukiNoteRow.ListName.ValueNullable is { } akatsukiNoteStringRow: ImGui.SameLine(); - ImGui.Text(akatsukiNoteStringRow.Unknown0.ExtractText()); + ImGui.Text(akatsukiNoteStringRow.Text.ExtractText()); break; } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs index 081f3ec96..6caf3286d 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs @@ -1,4 +1,4 @@ -using Dalamud.Bindings.ImGui; +using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects; using Dalamud.Interface.Utility; @@ -33,7 +33,7 @@ internal class TargetWidget : IDataWindowWidget { ImGui.Checkbox("Resolve GameData"u8, ref this.resolveGameData); - var clientState = Service.Get(); + var objectTable = Service.Get(); var targetMgr = Service.Get(); if (targetMgr.Target != null) @@ -80,7 +80,7 @@ internal class TargetWidget : IDataWindowWidget if (ImGui.Button("Clear FT"u8)) targetMgr.FocusTarget = null; - var localPlayer = clientState.LocalPlayer; + var localPlayer = objectTable.LocalPlayer; if (localPlayer != null) { diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/UldWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/UldWidget.cs index 8d31168e2..bc12f4d28 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/UldWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/UldWidget.cs @@ -27,7 +27,7 @@ internal class UldWidget : IDataWindowWidget { // 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"]; - private static readonly string[] ThemeBasePaths = ["ui/uld/", "ui/uld/light/", "ui/uld/third/", "ui/uld/fourth/"]; + 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 diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/VfsWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/VfsWidget.cs index f1f2476c9..f044b2989 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/VfsWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/VfsWidget.cs @@ -52,7 +52,7 @@ internal class VfsWidget : IDataWindowWidget for (var i = 0; i < this.reps; i++) { stopwatch.Restart(); - service.WriteAllBytes(path, data); + service.WriteAllBytesAsync(path, data).GetAwaiter().GetResult(); stopwatch.Stop(); acc += stopwatch.ElapsedMilliseconds; Log.Information("Turn {Turn} took {Ms}ms", i, stopwatch.ElapsedMilliseconds); @@ -70,7 +70,7 @@ internal class VfsWidget : IDataWindowWidget for (var i = 0; i < this.reps; i++) { stopwatch.Restart(); - service.ReadAllBytes(path); + service.ReadAllBytesAsync(path).GetAwaiter().GetResult(); stopwatch.Stop(); acc += stopwatch.ElapsedMilliseconds; Log.Information("Turn {Turn} took {Ms}ms", i, stopwatch.ElapsedMilliseconds); diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index 3536c9fe7..ac092bd25 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -302,8 +302,7 @@ internal class PluginInstallerWindow : Window, IDisposable this.profileManagerWidget.Reset(); - var config = Service.Get(); - if (this.staleDalamudNewVersion == null && !config.DalamudBetaKind.IsNullOrEmpty()) + if (this.staleDalamudNewVersion == null && !Util.GetActiveTrack().IsNullOrEmpty()) { Service.Get().GetVersionForCurrentTrack().ContinueWith(t => { @@ -311,10 +310,10 @@ internal class PluginInstallerWindow : Window, IDisposable return; var versionInfo = t.Result; - if (versionInfo.AssemblyVersion != Util.GetScmVersion() && - versionInfo.Track != "release" && - string.Equals(versionInfo.Key, config.DalamudBetaKey, StringComparison.OrdinalIgnoreCase)) + if (versionInfo.AssemblyVersion != Util.GetScmVersion()) + { this.staleDalamudNewVersion = versionInfo.AssemblyVersion; + } }); } } @@ -2076,10 +2075,18 @@ internal class PluginInstallerWindow : Window, IDisposable var isOpen = this.openPluginCollapsibles.Contains(index); var sectionSize = ImGuiHelpers.GlobalScale * 66; - var tapeCursor = ImGui.GetCursorPos(); ImGui.Separator(); + var childId = $"plugin_child_{label}_{plugin?.EffectiveWorkingPluginId}_{manifest.InternalName}"; + const ImGuiWindowFlags childFlags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse; + + using var pluginChild = ImRaii.Child(childId, new Vector2(ImGui.GetContentRegionAvail().X, sectionSize), false, childFlags); + if (!pluginChild) + { + return isOpen; + } + var startCursor = ImGui.GetCursorPos(); if (flags.HasFlag(PluginHeaderFlags.IsTesting)) @@ -2115,7 +2122,7 @@ internal class PluginInstallerWindow : Window, IDisposable } } - DrawCautionTape(tapeCursor + new Vector2(0, 1), new Vector2(ImGui.GetWindowWidth(), sectionSize + ImGui.GetStyle().ItemSpacing.Y), ImGuiHelpers.GlobalScale * 40, 20); + DrawCautionTape(startCursor + new Vector2(0, 1), new Vector2(ImGui.GetWindowWidth(), sectionSize + ImGui.GetStyle().ItemSpacing.Y), ImGuiHelpers.GlobalScale * 40, 20); } ImGui.PushStyleColor(ImGuiCol.Button, isOpen ? new Vector4(0.5f, 0.5f, 0.5f, 0.1f) : Vector4.Zero); @@ -2124,7 +2131,7 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.PushStyleColor(ImGuiCol.ButtonActive, new Vector4(0.5f, 0.5f, 0.5f, 0.35f)); ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 0); - ImGui.SetCursorPos(tapeCursor); + ImGui.SetCursorPos(startCursor); if (ImGui.Button($"###plugin{index}CollapsibleBtn", new Vector2(ImGui.GetContentRegionAvail().X, sectionSize + ImGui.GetStyle().ItemSpacing.Y))) { @@ -2446,10 +2453,11 @@ internal class PluginInstallerWindow : Window, IDisposable var configuration = Service.Get(); var pluginManager = Service.Get(); + var canUseTesting = pluginManager.CanUseTesting(manifest); var useTesting = pluginManager.UseTesting(manifest); var wasSeen = this.WasPluginSeen(manifest.InternalName); - var effectiveApiLevel = useTesting && manifest.TestingDalamudApiLevel != null ? manifest.TestingDalamudApiLevel.Value : manifest.DalamudApiLevel; + var effectiveApiLevel = useTesting ? manifest.TestingDalamudApiLevel.Value : manifest.DalamudApiLevel; var isOutdated = effectiveApiLevel < PluginManager.DalamudApiLevel; var isIncompatible = manifest.MinimumDalamudVersion != null && @@ -2479,7 +2487,7 @@ internal class PluginInstallerWindow : Window, IDisposable { label += Locs.PluginTitleMod_TestingExclusive; } - else if (configuration.DoPluginTest && PluginManager.HasTestingVersion(manifest)) + else if (canUseTesting) { label += Locs.PluginTitleMod_TestingAvailable; } @@ -2585,8 +2593,7 @@ internal class PluginInstallerWindow : Window, IDisposable var configuration = Service.Get(); var pluginManager = Service.Get(); - var hasTestingVersionAvailable = configuration.DoPluginTest && - PluginManager.HasTestingVersion(manifest); + var hasTestingVersionAvailable = configuration.DoPluginTest && manifest.IsAvailableForTesting; if (ImGui.BeginPopupContextItem("ItemContextMenu"u8)) { @@ -2681,8 +2688,7 @@ internal class PluginInstallerWindow : Window, IDisposable label += Locs.PluginTitleMod_TestingVersion; } - var hasTestingAvailable = this.pluginListAvailable.Any(x => x.InternalName == plugin.InternalName && - x.IsAvailableForTesting); + var hasTestingAvailable = this.pluginListAvailable.Any(x => x.InternalName == plugin.InternalName && x.IsAvailableForTesting); if (hasTestingAvailable && configuration.DoPluginTest && testingOptIn == null) { label += Locs.PluginTitleMod_TestingAvailable; @@ -3776,16 +3782,7 @@ internal class PluginInstallerWindow : Window, IDisposable private bool IsManifestFiltered(IPluginManifest manifest) { - var hasSearchString = !string.IsNullOrWhiteSpace(this.searchText); - var oldApi = (manifest.TestingDalamudApiLevel == null - || manifest.TestingDalamudApiLevel < PluginManager.DalamudApiLevel) - && manifest.DalamudApiLevel < PluginManager.DalamudApiLevel; - var installed = this.IsManifestInstalled(manifest).IsInstalled; - - if (oldApi && !hasSearchString && !installed) - return true; - - if (!hasSearchString) + if (string.IsNullOrWhiteSpace(this.searchText)) return false; return this.GetManifestSearchScore(manifest) < 1; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs b/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs index 86036b2ed..ea8cd0070 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs @@ -6,12 +6,13 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; -using Dalamud.Interface.Internal.Windows.SelfTest.Steps; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; using Dalamud.Logging.Internal; -using Lumina.Excel.Sheets; +using Dalamud.Plugin.SelfTest; +using Dalamud.Plugin.SelfTest.Internal; +using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.SelfTest; @@ -22,56 +23,23 @@ internal class SelfTestWindow : Window { private static readonly ModuleLog Log = new("AGING"); - private readonly List steps = - [ - new LoginEventSelfTestStep(), - new WaitFramesSelfTestStep(1000), - new FrameworkTaskSchedulerSelfTestStep(), - new EnterTerritorySelfTestStep(148, "Central Shroud"), - new ItemPayloadSelfTestStep(), - new ContextMenuSelfTestStep(), - new NamePlateSelfTestStep(), - new ActorTableSelfTestStep(), - new FateTableSelfTestStep(), - new AetheryteListSelfTestStep(), - new ConditionSelfTestStep(), - new ToastSelfTestStep(), - new TargetSelfTestStep(), - new KeyStateSelfTestStep(), - new GamepadStateSelfTestStep(), - new ChatSelfTestStep(), - new HoverSelfTestStep(), - new LuminaSelfTestStep(true), - new LuminaSelfTestStep(true), - new LuminaSelfTestStep(true), - new LuminaSelfTestStep(true), - new LuminaSelfTestStep(false), - new AddonLifecycleSelfTestStep(), - new PartyFinderSelfTestStep(), - new HandledExceptionSelfTestStep(), - new DutyStateSelfTestStep(), - new GameConfigSelfTestStep(), - new MarketBoardSelfTestStep(), - new SheetRedirectResolverSelfTestStep(), - new NounProcessorSelfTestStep(), - new SeStringEvaluatorSelfTestStep(), - new CompletionSelfTestStep(), - new LogoutEventSelfTestStep() - ]; + private readonly SelfTestRegistry selfTestRegistry; - private readonly Dictionary testIndexToResult = new(); + private List visibleSteps = new(); private bool selfTestRunning = false; - private int currentStep = 0; - private int scrollToStep = -1; - private DateTimeOffset lastTestStart; + private SelfTestGroup? currentTestGroup = null; + private SelfTestWithResults? currentStep = null; + private SelfTestWithResults? scrollToStep = null; /// /// Initializes a new instance of the class. /// - public SelfTestWindow() + /// An instance of . + public SelfTestWindow(SelfTestRegistry selfTestRegistry) : base("Dalamud Self-Test", ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse) { + this.selfTestRegistry = selfTestRegistry; this.Size = new Vector2(800, 800); this.SizeCondition = ImGuiCond.FirstUseEver; @@ -81,6 +49,55 @@ internal class SelfTestWindow : Window /// public override void Draw() { + // Initialize to first group if not set (first time drawing) + if (this.currentTestGroup == null) + { + this.currentTestGroup = this.selfTestRegistry.SelfTestGroups.FirstOrDefault(); // Should always be "Dalamud" + if (this.currentTestGroup != null) + { + this.SelectTestGroup(this.currentTestGroup); + } + } + + // Update visible steps based on current group + if (this.currentTestGroup != null) + { + this.visibleSteps = this.selfTestRegistry.SelfTests + .Where(test => test.Group == this.currentTestGroup.Name).ToList(); + + // Stop tests if no steps available or if current step is no longer valid + if (this.visibleSteps.Count == 0 || (this.currentStep != null && !this.visibleSteps.Contains(this.currentStep))) + { + this.StopTests(); + } + } + + using (var dropdown = ImRaii.Combo("###SelfTestGroupCombo"u8, this.currentTestGroup?.Name ?? string.Empty)) + { + if (dropdown) + { + foreach (var testGroup in this.selfTestRegistry.SelfTestGroups) + { + if (ImGui.Selectable(testGroup.Name)) + { + this.SelectTestGroup(testGroup); + } + + if (!testGroup.Loaded) + { + ImGui.SameLine(); + this.DrawUnloadedIcon(); + } + } + } + } + + if (this.currentTestGroup?.Loaded == false) + { + ImGui.SameLine(); + this.DrawUnloadedIcon(); + } + if (this.selfTestRunning) { if (ImGuiComponents.IconButton(FontAwesomeIcon.Stop)) @@ -92,13 +109,10 @@ internal class SelfTestWindow : Window if (ImGuiComponents.IconButton(FontAwesomeIcon.StepForward)) { - this.testIndexToResult[this.currentStep] = (SelfTestStepResult.NotRan, null); - this.steps[this.currentStep].CleanUp(); - this.currentStep++; + this.currentStep.Reset(); + this.MoveToNextTest(); this.scrollToStep = this.currentStep; - this.lastTestStart = DateTimeOffset.Now; - - if (this.currentStep >= this.steps.Count) + if (this.currentStep == null) { this.StopTests(); } @@ -106,38 +120,50 @@ internal class SelfTestWindow : Window } else { + var canRunTests = this.currentTestGroup?.Loaded == true && this.visibleSteps.Count > 0; + + using var disabled = ImRaii.Disabled(!canRunTests); if (ImGuiComponents.IconButton(FontAwesomeIcon.Play)) { this.selfTestRunning = true; - this.currentStep = 0; + this.currentStep = this.visibleSteps.FirstOrDefault(); this.scrollToStep = this.currentStep; - this.testIndexToResult.Clear(); - this.lastTestStart = DateTimeOffset.Now; + foreach (var test in this.visibleSteps) + { + test.Reset(); + } } } ImGui.SameLine(); - ImGui.Text($"Step: {this.currentStep} / {this.steps.Count}"); + var stepNumber = this.currentStep != null ? this.visibleSteps.IndexOf(this.currentStep) : 0; + ImGui.Text($"Step: {stepNumber} / {this.visibleSteps.Count}"); ImGui.Spacing(); + if (this.currentTestGroup?.Loaded == false) + { + ImGui.TextColoredWrapped(ImGuiColors.DalamudGrey, $"Plugin '{this.currentTestGroup.Name}' is unloaded. No tests available."); + ImGui.Spacing(); + } + this.DrawResultTable(); ImGui.Spacing(); - if (this.currentStep >= this.steps.Count) + if (this.currentStep == null) { if (this.selfTestRunning) { this.StopTests(); } - if (this.testIndexToResult.Any(x => x.Value.Result == SelfTestStepResult.Fail)) + if (this.visibleSteps.Any(test => test.Result == SelfTestStepResult.Fail)) { ImGui.TextColoredWrapped(ImGuiColors.DalamudRed, "One or more checks failed!"u8); } - else + else if (this.visibleSteps.All(test => test.Result == SelfTestStepResult.Pass)) { ImGui.TextColoredWrapped(ImGuiColors.HealerGreen, "All checks passed!"u8); } @@ -153,30 +179,14 @@ internal class SelfTestWindow : Window using var resultChild = ImRaii.Child("SelfTestResultChild"u8, ImGui.GetContentRegionAvail()); if (!resultChild) return; - var step = this.steps[this.currentStep]; - ImGui.Text($"Current: {step.Name}"); + ImGui.Text($"Current: {this.currentStep.Name}"); ImGuiHelpers.ScaledDummy(10); - SelfTestStepResult result; - try + this.currentStep.DrawAndStep(); + if (this.currentStep.Result != SelfTestStepResult.Waiting) { - result = step.RunStep(); - } - catch (Exception ex) - { - Log.Error(ex, $"Step failed: {step.Name}"); - result = SelfTestStepResult.Fail; - } - - if (result != SelfTestStepResult.Waiting) - { - var duration = DateTimeOffset.Now - this.lastTestStart; - this.testIndexToResult[this.currentStep] = (result, duration); - this.currentStep++; - this.scrollToStep = this.currentStep; - - this.lastTestStart = DateTimeOffset.Now; + this.MoveToNextTest(); } } @@ -202,85 +212,62 @@ internal class SelfTestWindow : Window ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableHeadersRow(); - for (var i = 0; i < this.steps.Count; i++) + foreach (var (step, index) in this.visibleSteps.WithIndex()) { - var step = this.steps[i]; ImGui.TableNextRow(); - if (this.selfTestRunning && this.currentStep == i) + if (this.selfTestRunning && this.currentStep == step) { ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, ImGui.GetColorU32(ImGuiCol.TableRowBgAlt)); } ImGui.TableSetColumnIndex(0); ImGui.AlignTextToFramePadding(); - ImGui.Text(i.ToString()); + ImGui.Text(index.ToString()); - if (this.selfTestRunning && this.scrollToStep == i) + if (this.selfTestRunning && this.scrollToStep == step) { ImGui.SetScrollHereY(); - this.scrollToStep = -1; + this.scrollToStep = null; } ImGui.TableSetColumnIndex(1); ImGui.AlignTextToFramePadding(); ImGui.Text(step.Name); - if (this.testIndexToResult.TryGetValue(i, out var result)) + ImGui.TableSetColumnIndex(2); + ImGui.AlignTextToFramePadding(); + switch (step.Result) { - ImGui.TableSetColumnIndex(2); - ImGui.AlignTextToFramePadding(); - - switch (result.Result) - { - case SelfTestStepResult.Pass: - ImGui.TextColored(ImGuiColors.HealerGreen, "PASS"u8); - break; - case SelfTestStepResult.Fail: - ImGui.TextColored(ImGuiColors.DalamudRed, "FAIL"u8); - break; - default: - ImGui.TextColored(ImGuiColors.DalamudGrey, "NR"u8); - break; - } - - ImGui.TableSetColumnIndex(3); - if (result.Duration.HasValue) - { - ImGui.AlignTextToFramePadding(); - ImGui.Text(this.FormatTimeSpan(result.Duration.Value)); - } - } - else - { - ImGui.TableSetColumnIndex(2); - ImGui.AlignTextToFramePadding(); - if (this.selfTestRunning && this.currentStep == i) - { + case SelfTestStepResult.Pass: + ImGui.TextColored(ImGuiColors.HealerGreen, "PASS"u8); + break; + case SelfTestStepResult.Fail: + ImGui.TextColored(ImGuiColors.DalamudRed, "FAIL"u8); + break; + case SelfTestStepResult.Waiting: ImGui.TextColored(ImGuiColors.DalamudGrey, "WAIT"u8); - } - else - { + break; + default: ImGui.TextColored(ImGuiColors.DalamudGrey, "NR"u8); - } + break; + } - ImGui.TableSetColumnIndex(3); + ImGui.TableSetColumnIndex(3); + if (step.Duration.HasValue) + { ImGui.AlignTextToFramePadding(); - if (this.selfTestRunning && this.currentStep == i) - { - ImGui.Text(this.FormatTimeSpan(DateTimeOffset.Now - this.lastTestStart)); - } + ImGui.Text(this.FormatTimeSpan(step.Duration.Value)); } ImGui.TableSetColumnIndex(4); - using var id = ImRaii.PushId($"selfTest{i}"); + using var id = ImRaii.PushId($"selfTest{index}"); if (ImGuiComponents.IconButton(FontAwesomeIcon.FastForward)) { this.StopTests(); - this.testIndexToResult.Remove(i); - this.currentStep = i; + this.currentStep = step; + this.currentStep.Reset(); this.selfTestRunning = true; - this.lastTestStart = DateTimeOffset.Now; } if (ImGui.IsItemHovered()) @@ -293,12 +280,14 @@ internal class SelfTestWindow : Window private void StopTests() { this.selfTestRunning = false; + this.currentStep = null; + this.scrollToStep = null; - foreach (var agingStep in this.steps) + foreach (var agingStep in this.visibleSteps) { try { - agingStep.CleanUp(); + agingStep.Finish(); } catch (Exception ex) { @@ -307,6 +296,46 @@ internal class SelfTestWindow : Window } } + /// + /// Makes the active test group. + /// + /// The test group to make active. + private void SelectTestGroup(SelfTestGroup testGroup) + { + this.currentTestGroup = testGroup; + this.StopTests(); + } + + /// + /// Move `currentTest` to the next test. If there are no tests left, set `currentTest` to null. + /// + private void MoveToNextTest() + { + if (this.currentStep == null) + { + this.currentStep = this.visibleSteps.FirstOrDefault(); + return; + } + + var currentIndex = this.visibleSteps.IndexOf(this.currentStep); + this.currentStep = this.visibleSteps.ElementAtOrDefault(currentIndex + 1); + } + + /// + /// Draws the unloaded plugin icon with tooltip. + /// + private void DrawUnloadedIcon() + { + ImGui.PushFont(UiBuilder.IconFont); + ImGui.TextColored(ImGuiColors.DalamudGrey, FontAwesomeIcon.Unlink.ToIconString()); + ImGui.PopFont(); + + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip("Plugin is unloaded"); + } + } + private string FormatTimeSpan(TimeSpan ts) { var str = ts.ToString("g", CultureInfo.InvariantCulture); diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ActorTableSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ActorTableSelfTestStep.cs index 0f0e10f10..013333935 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ActorTableSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ActorTableSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Objects; +using Dalamud.Plugin.SelfTest; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AddonLifecycleSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AddonLifecycleSelfTestStep.cs index 9ba3c4f2b..d9c9facc7 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AddonLifecycleSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AddonLifecycleSelfTestStep.cs @@ -3,6 +3,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Addon.Lifecycle; using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AetheryteListSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AetheryteListSelfTestStep.cs index 4d4f3fbb1..644c23e88 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AetheryteListSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/AetheryteListSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Aetherytes; +using Dalamud.Plugin.SelfTest; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ChatSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ChatSelfTestStep.cs index c2351cfd2..7a2631fbf 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ChatSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ChatSelfTestStep.cs @@ -2,6 +2,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/CompletionSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/CompletionSelfTestStep.cs index 463616362..a34b058bd 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/CompletionSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/CompletionSelfTestStep.cs @@ -1,6 +1,7 @@ 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 36e544040..89083da48 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ConditionSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ConditionSelfTestStep.cs @@ -1,5 +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 83003f21f..0fe5b4443 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ContextMenuSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ContextMenuSelfTestStep.cs @@ -8,6 +8,7 @@ using Dalamud.Game.ClientState.Objects.SubKinds; 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; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/DalamudSelfTest.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/DalamudSelfTest.cs new file mode 100644 index 000000000..d36aec742 --- /dev/null +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/DalamudSelfTest.cs @@ -0,0 +1,51 @@ +using Dalamud.Plugin.SelfTest.Internal; +using Lumina.Excel.Sheets; + +namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; + +/// +/// Class handling Dalamud self-test registration. +/// +[ServiceManager.EarlyLoadedService] +internal class DalamudSelfTest : IServiceType +{ + [ServiceManager.ServiceConstructor] + private DalamudSelfTest(SelfTestRegistry registry) + { + registry.RegisterDalamudSelfTestSteps([ + new LoginEventSelfTestStep(), + new WaitFramesSelfTestStep(1000), + new FrameworkTaskSchedulerSelfTestStep(), + new EnterTerritorySelfTestStep(148, "Central Shroud"), + new ItemPayloadSelfTestStep(), + new ContextMenuSelfTestStep(), + new NamePlateSelfTestStep(), + new ActorTableSelfTestStep(), + new FateTableSelfTestStep(), + new AetheryteListSelfTestStep(), + new ConditionSelfTestStep(), + new ToastSelfTestStep(), + new TargetSelfTestStep(), + new KeyStateSelfTestStep(), + new GamepadStateSelfTestStep(), + new ChatSelfTestStep(), + new HoverSelfTestStep(), + new LuminaSelfTestStep(true), + new LuminaSelfTestStep(true), + new LuminaSelfTestStep(true), + new LuminaSelfTestStep(true), + new LuminaSelfTestStep(false), + new AddonLifecycleSelfTestStep(), + new PartyFinderSelfTestStep(), + new HandledExceptionSelfTestStep(), + new DutyStateSelfTestStep(), + new GameConfigSelfTestStep(), + new MarketBoardSelfTestStep(), + new SheetRedirectResolverSelfTestStep(), + new NounProcessorSelfTestStep(), + new SeStringEvaluatorSelfTestStep(), + new CompletionSelfTestStep(), + new LogoutEventSelfTestStep() + ]); + } +} diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/DutyStateSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/DutyStateSelfTestStep.cs index 9c40aa345..bf3ffa5c9 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/DutyStateSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/DutyStateSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.DutyState; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/EnterTerritorySelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/EnterTerritorySelfTestStep.cs index ff43d7ae3..4cce1e377 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/EnterTerritorySelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/EnterTerritorySelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FateTableSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FateTableSelfTestStep.cs index ba5f769c2..a413f4932 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FateTableSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FateTableSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Fates; +using Dalamud.Plugin.SelfTest; using Dalamud.Utility; 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 d952be419..eb6909fa7 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FrameworkTaskSchedulerSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/FrameworkTaskSchedulerSelfTestStep.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using Dalamud.Game; +using Dalamud.Plugin.SelfTest; using Dalamud.Utility; using Microsoft.VisualBasic.Logging; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GameConfigSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GameConfigSelfTestStep.cs index 67faec5c9..fa29068c5 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GameConfigSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GameConfigSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Config; +using Dalamud.Plugin.SelfTest; 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 04684d521..d272032e7 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GamepadStateSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/GamepadStateSelfTestStep.cs @@ -2,7 +2,7 @@ using System.Linq; using Dalamud.Game.ClientState.GamePad; using Dalamud.Interface.Utility; - +using Dalamud.Plugin.SelfTest; using Lumina.Text.Payloads; using LSeStringBuilder = Lumina.Text.SeStringBuilder; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HandledExceptionSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HandledExceptionSelfTestStep.cs index 9757481e4..c9f7df8a5 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HandledExceptionSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HandledExceptionSelfTestStep.cs @@ -1,5 +1,7 @@ using System.Runtime.InteropServices; +using Dalamud.Plugin.SelfTest; + namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; /// diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HoverSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HoverSelfTestStep.cs index 8c469d210..e33f16e7f 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HoverSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/HoverSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ItemPayloadSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ItemPayloadSelfTestStep.cs index 261318d6b..c441796a2 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ItemPayloadSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ItemPayloadSelfTestStep.cs @@ -1,6 +1,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui; using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Plugin.SelfTest; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/KeyStateSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/KeyStateSelfTestStep.cs index 59ffb33ae..2e936884e 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/KeyStateSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/KeyStateSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Keys; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LoginEventSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LoginEventSelfTestStep.cs index b5a6337ce..97df6bf94 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LoginEventSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LoginEventSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LogoutEventSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LogoutEventSelfTestStep.cs index c8788968e..bc337cf3e 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LogoutEventSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LogoutEventSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LuminaSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LuminaSelfTestStep.cs index ff9649a14..741dd71b1 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LuminaSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/LuminaSelfTestStep.cs @@ -1,4 +1,5 @@ using Dalamud.Data; +using Dalamud.Plugin.SelfTest; using Dalamud.Utility; using Lumina.Excel; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/MarketBoardSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/MarketBoardSelfTestStep.cs index a7e9eb681..6a45f343a 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/MarketBoardSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/MarketBoardSelfTestStep.cs @@ -5,6 +5,7 @@ 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 120335c04..9cc6045a6 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NamePlateSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NamePlateSelfTestStep.cs @@ -4,6 +4,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui.NamePlate; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NounProcessorSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NounProcessorSelfTestStep.cs index 682fe7222..ccccc691c 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NounProcessorSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/NounProcessorSelfTestStep.cs @@ -2,6 +2,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game; using Dalamud.Game.Text.Noun; using Dalamud.Game.Text.Noun.Enums; +using Dalamud.Plugin.SelfTest; using LSheets = Lumina.Excel.Sheets; @@ -190,6 +191,29 @@ 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/PartyFinderSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/PartyFinderSelfTestStep.cs index d8bdd6d53..052f65a21 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/PartyFinderSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/PartyFinderSelfTestStep.cs @@ -1,6 +1,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui.PartyFinder; using Dalamud.Game.Gui.PartyFinder.Types; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SeStringEvaluatorSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SeStringEvaluatorSelfTestStep.cs index 8e66dd5cf..56a350229 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SeStringEvaluatorSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SeStringEvaluatorSelfTestStep.cs @@ -1,6 +1,10 @@ using Dalamud.Bindings.ImGui; +using Dalamud.Configuration.Internal; using Dalamud.Game.ClientState; +using Dalamud.Game.ClientState.Objects; using Dalamud.Game.Text.Evaluator; +using Dalamud.Game.Text.SeStringHandling.Payloads; +using Dalamud.Plugin.SelfTest; using Lumina.Text.ReadOnly; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; @@ -48,8 +52,8 @@ internal class SeStringEvaluatorSelfTestStep : ISelfTestStep // that it returned the local players name by using its EntityId, // and that it didn't include the world name by checking the HomeWorldId against AgentLobby.Instance()->LobbyData.HomeWorldId. - var clientState = Service.Get(); - var localPlayer = clientState.LocalPlayer; + var objectTable = Service.Get(); + var localPlayer = objectTable.LocalPlayer; if (localPlayer is null) { ImGui.Text("You need to be logged in for this step."u8); @@ -75,6 +79,55 @@ internal class SeStringEvaluatorSelfTestStep : ISelfTestStep return SelfTestStepResult.Waiting; } + this.step++; + break; + + case 2: + ImGui.Text("Checking AutoTranslatePayload.Text results..."u8); + + var config = Service.Get(); + var originalLanguageOverride = config.LanguageOverride; + + Span<(string Language, uint Group, uint Key, string ExpectedText)> tests = [ + ("en", 49u, 209u, " albino karakul "), // Mount + ("en", 62u, 116u, " /echo "), // TextCommand - testing Command + ("en", 62u, 143u, " /dutyfinder "), // TextCommand - testing Alias over Command + ("en", 65u, 67u, " Minion of Light "), // Companion - testing noun handling for the german language (special case) + ("en", 71u, 7u, " Phantom Geomancer "), // MKDSupportJob + + ("de", 49u, 209u, " Albino-Karakul "), // Mount + ("de", 62u, 115u, " /freiegesellschaft "), // TextCommand - testing Alias over Command + ("de", 62u, 116u, " /echo "), // TextCommand - testing Command + ("de", 65u, 67u, " Begleiter des Lichts "), // Companion - testing noun handling for the german language (special case) + ("de", 71u, 7u, " Phantom-Geomant "), // MKDSupportJob + ]; + + try + { + foreach (var (language, group, key, expectedText) in tests) + { + config.LanguageOverride = language; + + var payload = new AutoTranslatePayload(group, key); + + if (payload.Text != expectedText) + { + ImGui.Text($"Test failed for Group {group}, Key {key}"); + ImGui.Text($"Expected: {expectedText}"); + ImGui.Text($"Got: {payload.Text}"); + + if (ImGui.Button("Continue"u8)) + return SelfTestStepResult.Fail; + + return SelfTestStepResult.Waiting; + } + } + } + finally + { + config.LanguageOverride = originalLanguageOverride; + } + return SelfTestStepResult.Pass; } diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SheetRedirectResolverSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SheetRedirectResolverSelfTestStep.cs index cd2e270db..c285fda46 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SheetRedirectResolverSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/SheetRedirectResolverSelfTestStep.cs @@ -3,6 +3,7 @@ using System.Runtime.InteropServices; 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/SelfTest/Steps/TargetSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/TargetSelfTestStep.cs index f3a1e2aab..4e5e287a4 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/TargetSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/TargetSelfTestStep.cs @@ -2,6 +2,7 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ToastSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ToastSelfTestStep.cs index 4c66e7380..793473ecc 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ToastSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ToastSelfTestStep.cs @@ -1,5 +1,6 @@ using Dalamud.Bindings.ImGui; using Dalamud.Game.Gui.Toast; +using Dalamud.Plugin.SelfTest; namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/WaitFramesSelfTestStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/WaitFramesSelfTestStep.cs index 35c64376d..b0cc4f454 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/WaitFramesSelfTestStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/Steps/WaitFramesSelfTestStep.cs @@ -1,3 +1,5 @@ +using Dalamud.Plugin.SelfTest; + namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; /// diff --git a/Dalamud/Interface/Internal/Windows/Settings/SettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/SettingsEntry.cs index 4cb239da3..3f53be07a 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/SettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/SettingsEntry.cs @@ -1,14 +1,16 @@ -namespace Dalamud.Interface.Internal.Windows.Settings; +using Dalamud.Utility.Internal; + +namespace Dalamud.Interface.Internal.Windows.Settings; /// /// Basic, drawable settings entry. /// -public abstract class SettingsEntry +internal abstract class SettingsEntry { /// /// Gets or sets the public, searchable name of this settings entry. /// - public string? Name { get; protected set; } + public LazyLoc Name { get; protected set; } /// /// Gets or sets a value indicating whether this entry is valid. diff --git a/Dalamud/Interface/Internal/Windows/Settings/SettingsTab.cs b/Dalamud/Interface/Internal/Windows/Settings/SettingsTab.cs index bd4a702f5..e43e3c83a 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/SettingsTab.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/SettingsTab.cs @@ -1,19 +1,19 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using Dalamud.Interface.Utility; namespace Dalamud.Interface.Internal.Windows.Settings; [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")] -public abstract class SettingsTab : IDisposable +internal abstract class SettingsTab : IDisposable { public abstract SettingsEntry[] Entries { get; } public abstract string Title { get; } - public bool IsOpen { get; set; } = false; + public abstract SettingsOpenKind Kind { get; } - public virtual bool IsVisible { get; } = true; + public bool IsOpen { get; set; } = false; public virtual void OnOpen() { diff --git a/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs b/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs index 75cbeb836..581ef3746 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs @@ -17,20 +17,19 @@ namespace Dalamud.Interface.Internal.Windows.Settings; /// /// The window that allows for general configuration of Dalamud itself. /// -internal class SettingsWindow : Window +internal sealed class SettingsWindow : Window { private readonly SettingsTab[] tabs; - private string searchInput = string.Empty; private bool isSearchInputPrefilled = false; - private SettingsTab setActiveTab = null!; + private SettingsTab? setActiveTab; /// /// Initializes a new instance of the class. /// public SettingsWindow() - : base(Loc.Localize("DalamudSettingsHeader", "Dalamud Settings") + "###XlSettings2", ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollbar) + : base(Title, ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollbar) { this.Size = new Vector2(740, 550); this.SizeConstraints = new WindowSizeConstraints() @@ -52,6 +51,10 @@ internal class SettingsWindow : Window ]; } + private static string Title => Loc.Localize("DalamudSettingsHeader", "Dalamud Settings") + "###XlSettings2"; + + private SettingsTab? CurrentlyOpenTab => this.tabs.FirstOrDefault(tab => tab.IsOpen); + /// /// Open the settings window to the tab specified by . /// @@ -59,7 +62,7 @@ internal class SettingsWindow : Window public void OpenTo(SettingsOpenKind kind) { this.IsOpen = true; - this.SetOpenTab(kind); + this.setActiveTab = this.tabs.Single(tab => tab.Kind == kind); } /// @@ -83,12 +86,19 @@ internal class SettingsWindow : Window /// public override void OnOpen() { + var localization = Service.Get(); + foreach (var settingsTab in this.tabs) { settingsTab.Load(); } - if (!this.isSearchInputPrefilled) this.searchInput = string.Empty; + if (!this.isSearchInputPrefilled) + { + this.searchInput = string.Empty; + } + + localization.LocalizationChanged += this.OnLocalizationChanged; base.OnOpen(); } @@ -99,6 +109,7 @@ internal class SettingsWindow : Window var configuration = Service.Get(); var interfaceManager = Service.Get(); var fontAtlasFactory = Service.Get(); + var localization = Service.Get(); var scaleChanged = !Equals(ImGui.GetIO().FontGlobalScale, configuration.GlobalUiScale); var rebuildFont = !Equals(fontAtlasFactory.DefaultFontSpec, configuration.DefaultFontSpec); @@ -107,7 +118,7 @@ internal class SettingsWindow : Window ImGui.GetIO().FontGlobalScale = configuration.GlobalUiScale; if (scaleChanged) { - Service.Get().InvokeGlobalScaleChanged(); + interfaceManager.InvokeGlobalScaleChanged(); } fontAtlasFactory.DefaultFontSpecOverride = null; @@ -115,7 +126,7 @@ internal class SettingsWindow : Window if (rebuildFont) { interfaceManager.RebuildFonts(); - Service.Get().InvokeFontChanged(); + interfaceManager.InvokeFontChanged(); } foreach (var settingsTab in this.tabs) @@ -133,98 +144,29 @@ internal class SettingsWindow : Window this.isSearchInputPrefilled = false; this.searchInput = string.Empty; } + + localization.LocalizationChanged -= this.OnLocalizationChanged; } /// public override void Draw() { + ImGui.SetNextItemWidth(-1); + using (ImRaii.Disabled(this.CurrentlyOpenTab is SettingsTabAbout)) + ImGui.InputTextWithHint("###searchInput"u8, Loc.Localize("DalamudSettingsSearchPlaceholder", "Search for settings..."), ref this.searchInput, 100, ImGuiInputTextFlags.AutoSelectAll); + ImGui.Spacing(); + var windowSize = ImGui.GetWindowSize(); - if (ImGui.BeginTabBar("###settingsTabs"u8)) + using (var tabBar = ImRaii.TabBar("###settingsTabs"u8)) { - if (string.IsNullOrEmpty(this.searchInput)) + if (tabBar) { - foreach (var settingsTab in this.tabs.Where(x => x.IsVisible)) - { - var flags = ImGuiTabItemFlags.NoCloseWithMiddleMouseButton; - if (this.setActiveTab == settingsTab) - { - flags |= ImGuiTabItemFlags.SetSelected; - this.setActiveTab = null; - } - - using var tab = ImRaii.TabItem(settingsTab.Title, flags); - if (tab) - { - if (!settingsTab.IsOpen) - { - settingsTab.IsOpen = true; - settingsTab.OnOpen(); - } - - // Don't add padding for the about tab(credits) - { - using var padding = ImRaii.PushStyle( - ImGuiStyleVar.WindowPadding, - new Vector2(2, 2), - settingsTab is not SettingsTabAbout); - using var borderColor = ImRaii.PushColor( - ImGuiCol.Border, - ImGui.GetColorU32(ImGuiCol.ChildBg)); - using var tabChild = ImRaii.Child( - $"###settings_scrolling_{settingsTab.Title}", - new Vector2(-1, -1), - true); - if (tabChild) - settingsTab.Draw(); - } - - settingsTab.PostDraw(); - } - else if (settingsTab.IsOpen) - { - settingsTab.IsOpen = false; - settingsTab.OnClose(); - } - } + if (string.IsNullOrEmpty(this.searchInput)) + this.DrawTabs(); + else + this.DrawSearchResults(); } - else - { - if (ImGui.BeginTabItem("Search Results"u8)) - { - var any = false; - - foreach (var settingsTab in this.tabs.Where(x => x.IsVisible)) - { - var eligible = settingsTab.Entries.Where(x => !x.Name.IsNullOrEmpty() && x.Name.ToLowerInvariant().Contains(this.searchInput.ToLowerInvariant())).ToArray(); - - if (!eligible.Any()) - continue; - - any = true; - - ImGui.TextColored(ImGuiColors.DalamudGrey, settingsTab.Title); - ImGui.Dummy(new Vector2(5)); - - foreach (var settingsTabEntry in eligible) - { - settingsTabEntry.Draw(); - ImGuiHelpers.ScaledDummy(3); - } - - ImGui.Separator(); - - ImGui.Dummy(new Vector2(10)); - } - - if (!any) - ImGui.TextColored(ImGuiColors.DalamudGrey, "No results found..."u8); - - ImGui.EndTabItem(); - } - } - - ImGui.EndTabBar(); } ImGui.SetCursorPos(windowSize - ImGuiHelpers.ScaledVector2(70)); @@ -256,10 +198,88 @@ internal class SettingsWindow : Window } } } + } - ImGui.SetCursorPos(new Vector2(windowSize.X - 250, ImGui.GetTextLineHeightWithSpacing() + (ImGui.GetStyle().FramePadding.Y * 2))); - ImGui.SetNextItemWidth(240); - ImGui.InputTextWithHint("###searchInput"u8, "Search for settings..."u8, ref this.searchInput, 100); + private void DrawTabs() + { + foreach (var settingsTab in this.tabs) + { + var flags = ImGuiTabItemFlags.NoCloseWithMiddleMouseButton; + + if (this.setActiveTab == settingsTab) + { + flags |= ImGuiTabItemFlags.SetSelected; + this.setActiveTab = null; + } + + using var tab = ImRaii.TabItem(settingsTab.Title, flags); + if (tab) + { + if (!settingsTab.IsOpen) + { + settingsTab.IsOpen = true; + settingsTab.OnOpen(); + } + + // Don't add padding for the About tab (credits) + { + using var padding = ImRaii.PushStyle( + ImGuiStyleVar.WindowPadding, + new Vector2(2, 2), + settingsTab is not SettingsTabAbout); + using var borderColor = ImRaii.PushColor( + ImGuiCol.Border, + ImGui.GetColorU32(ImGuiCol.ChildBg)); + using var tabChild = ImRaii.Child( + $"###settings_scrolling_{settingsTab.Title}", + new Vector2(-1, -1), + true); + if (tabChild) + settingsTab.Draw(); + } + + settingsTab.PostDraw(); + } + else if (settingsTab.IsOpen) + { + settingsTab.IsOpen = false; + settingsTab.OnClose(); + } + } + } + + private void DrawSearchResults() + { + using var tab = ImRaii.TabItem(Loc.Localize("DalamudSettingsSearchResults", "Search Results")); + if (!tab) return; + + var any = false; + + foreach (var settingsTab in this.tabs) + { + var eligible = settingsTab.Entries.Where(x => !x.Name.Key.IsNullOrEmpty() && x.Name.ToString().Contains(this.searchInput, StringComparison.InvariantCultureIgnoreCase)); + + if (!eligible.Any()) + continue; + + any |= true; + + ImGui.TextColored(ImGuiColors.DalamudGrey, settingsTab.Title); + ImGui.Dummy(new Vector2(5)); + + foreach (var settingsTabEntry in eligible) + { + settingsTabEntry.Draw(); + ImGuiHelpers.ScaledDummy(3); + } + + ImGui.Separator(); + + ImGui.Dummy(new Vector2(10)); + } + + if (!any) + ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsNoSearchResultsFound", "No results found...")); } private void Save() @@ -301,17 +321,9 @@ internal class SettingsWindow : Window Service.Get().RebuildFonts(); } - private void SetOpenTab(SettingsOpenKind kind) + private void OnLocalizationChanged(string langCode) { - this.setActiveTab = kind switch - { - SettingsOpenKind.General => this.tabs[0], - SettingsOpenKind.LookAndFeel => this.tabs[1], - SettingsOpenKind.AutoUpdates => this.tabs[2], - SettingsOpenKind.ServerInfoBar => this.tabs[3], - SettingsOpenKind.Experimental => this.tabs[4], - SettingsOpenKind.About => this.tabs[5], - _ => throw new ArgumentOutOfRangeException(nameof(kind), kind, null), - }; + this.WindowName = Title; + this.setActiveTab = this.CurrentlyOpenTab; } } diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAbout.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAbout.cs index 5b7fc7227..74b9b0fd7 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAbout.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAbout.cs @@ -20,7 +20,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.UI; namespace Dalamud.Interface.Internal.Windows.Settings.Tabs; [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")] -public class SettingsTabAbout : SettingsTab +internal sealed class SettingsTabAbout : SettingsTab { private const float CreditFps = 60.0f; private const string ThankYouText = "Thank you!"; @@ -209,10 +209,12 @@ Contribute at: https://github.com/goatcorp/Dalamud .CreateFontAtlas(nameof(SettingsTabAbout), FontAtlasAutoRebuildMode.Async); } - public override SettingsEntry[] Entries { get; } = { }; - public override string Title => Loc.Localize("DalamudAbout", "About"); + public override SettingsOpenKind Kind => SettingsOpenKind.About; + + public override SettingsEntry[] Entries { get; } = []; + /// public override unsafe void OnOpen() { @@ -287,7 +289,7 @@ Contribute at: https://github.com/goatcorp/Dalamud var windowX = ImGui.GetWindowSize().X; - foreach (var creditsLine in this.creditsText.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None)) + foreach (var creditsLine in this.creditsText.Split(["\r\n", "\r", "\n"], StringSplitOptions.None)) { var lineLenX = ImGui.CalcTextSize(creditsLine).X; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAutoUpdate.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAutoUpdate.cs index b25cdb10b..6b99c5c24 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAutoUpdate.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAutoUpdate.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Numerics; @@ -18,7 +18,7 @@ using Dalamud.Plugin.Internal.Types; namespace Dalamud.Interface.Internal.Windows.Settings.Tabs; [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")] -public class SettingsTabAutoUpdates : SettingsTab +internal sealed class SettingsTabAutoUpdates : SettingsTab { private AutoUpdateBehavior behavior; private bool updateDisabledPlugins; @@ -31,6 +31,8 @@ public class SettingsTabAutoUpdates : SettingsTab public override string Title => Loc.Localize("DalamudSettingsAutoUpdates", "Auto-Updates"); + public override SettingsOpenKind Kind => SettingsOpenKind.AutoUpdates; + public override void Draw() { ImGui.TextColoredWrapped(ImGuiColors.DalamudWhite, Loc.Localize("DalamudSettingsAutoUpdateHint", diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabDtr.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabDtr.cs index 7cd303587..4b055b35b 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabDtr.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabDtr.cs @@ -14,17 +14,19 @@ using Dalamud.Interface.Utility; namespace Dalamud.Interface.Internal.Windows.Settings.Tabs; [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")] -public class SettingsTabDtr : SettingsTab +internal sealed class SettingsTabDtr : SettingsTab { private List? dtrOrder; private List? dtrIgnore; private int dtrSpacing; private bool dtrSwapDirection; - public override SettingsEntry[] Entries { get; } = Array.Empty(); - public override string Title => Loc.Localize("DalamudSettingsServerInfoBar", "Server Info Bar"); + public override SettingsOpenKind Kind => SettingsOpenKind.ServerInfoBar; + + public override SettingsEntry[] Entries { get; } = []; + public override void Draw() { ImGui.TextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingServerInfoBarHint", "Plugins can put additional information into your server information bar(where world & time can be seen).\nYou can reorder and disable these here.")); @@ -125,8 +127,8 @@ public class SettingsTabDtr : SettingsTab ImGui.GetIO().MousePos = moveMouseTo[moveMouseToIndex]; } - configuration.DtrOrder = order.Concat(orderLeft).ToList(); - configuration.DtrIgnore = ignore.Concat(ignoreLeft).ToList(); + configuration.DtrOrder = [.. order, .. orderLeft]; + configuration.DtrIgnore = [.. ignore, .. ignoreLeft]; if (isOrderChange) dtrBar.ApplySort(); diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs index d3b741142..4ba5bed94 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabExperimental.cs @@ -6,11 +6,11 @@ using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; using Dalamud.Interface.Internal.ReShadeHandling; -using Dalamud.Interface.Internal.Windows.PluginInstaller; using Dalamud.Interface.Internal.Windows.Settings.Widgets; using Dalamud.Interface.Utility; using Dalamud.Plugin.Internal; using Dalamud.Utility; +using Dalamud.Utility.Internal; namespace Dalamud.Interface.Internal.Windows.Settings.Tabs; @@ -18,33 +18,28 @@ namespace Dalamud.Interface.Internal.Windows.Settings.Tabs; "StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")] -public class SettingsTabExperimental : SettingsTab +internal sealed class SettingsTabExperimental : SettingsTab { + public override string Title => Loc.Localize("DalamudSettingsExperimental", "Experimental"); + + public override SettingsOpenKind Kind => SettingsOpenKind.Experimental; + public override SettingsEntry[] Entries { get; } = [ new SettingsEntry( - Loc.Localize("DalamudSettingsPluginTest", "Get plugin testing builds"), - string.Format( - Loc.Localize( - "DalamudSettingsPluginTestHint", - "Receive testing prereleases for selected plugins.\nTo opt-in to testing builds for a plugin, you have to right click it in the \"{0}\" tab of the plugin installer and select \"{1}\"."), - PluginCategoryManager.Locs.Group_Installed, - PluginInstallerWindow.Locs.PluginContext_TestingOptIn), + LazyLoc.Localize("DalamudSettingsPluginTest", "Get plugin testing builds"), + LazyLoc.Localize("DalamudSettingsPluginTestHint", "Receive testing prereleases for selected plugins.\nTo opt-in to testing builds for a plugin, you have to right click it in the \"Installed Plugins\" tab of the plugin installer and select \"Receive plugin testing versions\"."), c => c.DoPluginTest, (v, c) => c.DoPluginTest = v), new HintSettingsEntry( - Loc.Localize( - "DalamudSettingsPluginTestWarning", - "Testing plugins may contain bugs or crash your game. Please only enable this if you are aware of the risks."), + LazyLoc.Localize("DalamudSettingsPluginTestWarning", "Testing plugins may contain bugs or crash your game. Please only enable this if you are aware of the risks."), ImGuiColors.DalamudRed), new GapSettingsEntry(5), new ButtonSettingsEntry( - Loc.Localize("DalamudSettingsClearHidden", "Clear hidden plugins"), - Loc.Localize( - "DalamudSettingsClearHiddenHint", - "Restore plugins you have previously hidden from the plugin installer."), + LazyLoc.Localize("DalamudSettingsClearHidden", "Clear hidden plugins"), + LazyLoc.Localize("DalamudSettingsClearHiddenHint", "Restore plugins you have previously hidden from the plugin installer."), () => { Service.Get().HiddenPluginInternalName.Clear(); @@ -56,23 +51,16 @@ public class SettingsTabExperimental : SettingsTab new DevPluginsSettingsEntry(), new SettingsEntry( - Loc.Localize( - "DalamudSettingEnableImGuiAsserts", - "Enable ImGui asserts"), - Loc.Localize( - "DalamudSettingEnableImGuiAssertsHint", + LazyLoc.Localize("DalamudSettingEnableImGuiAsserts", "Enable ImGui asserts"), + LazyLoc.Localize("DalamudSettingEnableImGuiAssertsHint", "If this setting is enabled, a window containing further details will be shown when an internal assertion in ImGui fails.\nWe recommend enabling this when developing plugins. " + "This setting does not persist and will reset when the game restarts.\nUse the setting below to enable it at startup."), c => Service.Get().ShowAsserts, (v, _) => Service.Get().ShowAsserts = v), new SettingsEntry( - Loc.Localize( - "DalamudSettingEnableImGuiAssertsAtStartup", - "Always enable ImGui asserts at startup"), - Loc.Localize( - "DalamudSettingEnableImGuiAssertsAtStartupHint", - "This will enable ImGui asserts every time the game starts."), + LazyLoc.Localize("DalamudSettingEnableImGuiAssertsAtStartup", "Always enable ImGui asserts at startup"), + LazyLoc.Localize("DalamudSettingEnableImGuiAssertsAtStartupHint", "This will enable ImGui asserts every time the game starts."), c => c.ImGuiAssertsEnabledAtStartup ?? false, (v, c) => c.ImGuiAssertsEnabledAtStartup = v), @@ -83,10 +71,8 @@ public class SettingsTabExperimental : SettingsTab new GapSettingsEntry(5, true), new EnumSettingsEntry( - Loc.Localize("DalamudSettingsReShadeHandlingMode", "ReShade handling mode"), - Loc.Localize( - "DalamudSettingsReShadeHandlingModeHint", - "You may try different options to work around problems you may encounter.\nRestart is required for changes to take effect."), + LazyLoc.Localize("DalamudSettingsReShadeHandlingMode", "ReShade handling mode"), + LazyLoc.Localize("DalamudSettingsReShadeHandlingModeHint", "You may try different options to work around problems you may encounter.\nRestart is required for changes to take effect."), c => c.ReShadeHandlingMode, (v, c) => c.ReShadeHandlingMode = v, fallbackValue: ReShadeHandlingMode.Default, @@ -146,8 +132,6 @@ public class SettingsTabExperimental : SettingsTab */ ]; - public override string Title => Loc.Localize("DalamudSettingsExperimental", "Experimental"); - public override void Draw() { base.Draw(); diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabGeneral.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabGeneral.cs index 5e3648ac6..0daa52630 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabGeneral.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabGeneral.cs @@ -1,23 +1,29 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using CheapLoc; + using Dalamud.Game.Text; using Dalamud.Interface.Internal.Windows.Settings.Widgets; +using Dalamud.Utility.Internal; namespace Dalamud.Interface.Internal.Windows.Settings.Tabs; [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")] -public class SettingsTabGeneral : SettingsTab +internal sealed class SettingsTabGeneral : SettingsTab { + public override string Title => Loc.Localize("DalamudSettingsGeneral", "General"); + + public override SettingsOpenKind Kind => SettingsOpenKind.General; + public override SettingsEntry[] Entries { get; } = - { + [ new LanguageChooserSettingsEntry(), new GapSettingsEntry(5), new EnumSettingsEntry( - Loc.Localize("DalamudSettingsChannel", "Dalamud Chat Channel"), - Loc.Localize("DalamudSettingsChannelHint", "Select the chat channel that is to be used for general Dalamud messages."), + LazyLoc.Localize("DalamudSettingsChannel", "Dalamud Chat Channel"), + LazyLoc.Localize("DalamudSettingsChannelHint", "Select the chat channel that is to be used for general Dalamud messages."), c => c.GeneralChatType, (v, c) => c.GeneralChatType = v, warning: v => @@ -33,49 +39,47 @@ public class SettingsTabGeneral : SettingsTab new GapSettingsEntry(5), new SettingsEntry( - Loc.Localize("DalamudSettingsWaitForPluginsOnStartup", "Wait for plugins before game loads"), - Loc.Localize("DalamudSettingsWaitForPluginsOnStartupHint", "Do not let the game load, until plugins are loaded."), + LazyLoc.Localize("DalamudSettingsWaitForPluginsOnStartup", "Wait for plugins before game loads"), + LazyLoc.Localize("DalamudSettingsWaitForPluginsOnStartupHint", "Do not let the game load, until plugins are loaded."), c => c.IsResumeGameAfterPluginLoad, (v, c) => c.IsResumeGameAfterPluginLoad = v), new SettingsEntry( - Loc.Localize("DalamudSettingsFlash", "Flash FFXIV window on duty pop"), - Loc.Localize("DalamudSettingsFlashHint", "Flash the FFXIV window in your task bar when a duty is ready."), + LazyLoc.Localize("DalamudSettingsFlash", "Flash FFXIV window on duty pop"), + LazyLoc.Localize("DalamudSettingsFlashHint", "Flash the FFXIV window in your task bar when a duty is ready."), c => c.DutyFinderTaskbarFlash, (v, c) => c.DutyFinderTaskbarFlash = v), new SettingsEntry( - Loc.Localize("DalamudSettingsDutyFinderMessage", "Chatlog message on duty pop"), - Loc.Localize("DalamudSettingsDutyFinderMessageHint", "Send a message in FFXIV chat when a duty is ready."), + LazyLoc.Localize("DalamudSettingsDutyFinderMessage", "Chatlog message on duty pop"), + LazyLoc.Localize("DalamudSettingsDutyFinderMessageHint", "Send a message in FFXIV chat when a duty is ready."), c => c.DutyFinderChatMessage, (v, c) => c.DutyFinderChatMessage = v), new SettingsEntry( - Loc.Localize("DalamudSettingsPrintDalamudWelcomeMsg", "Display Dalamud's welcome message"), - Loc.Localize("DalamudSettingsPrintDalamudWelcomeMsgHint", "Display Dalamud's welcome message in FFXIV chat when logging in with a character."), + LazyLoc.Localize("DalamudSettingsPrintDalamudWelcomeMsg", "Display Dalamud's welcome message"), + LazyLoc.Localize("DalamudSettingsPrintDalamudWelcomeMsgHint", "Display Dalamud's welcome message in FFXIV chat when logging in with a character."), c => c.PrintDalamudWelcomeMsg, (v, c) => c.PrintDalamudWelcomeMsg = v), new SettingsEntry( - Loc.Localize("DalamudSettingsPrintPluginsWelcomeMsg", "Display loaded plugins in the welcome message"), - Loc.Localize("DalamudSettingsPrintPluginsWelcomeMsgHint", "Display loaded plugins in FFXIV chat when logging in with a character."), + LazyLoc.Localize("DalamudSettingsPrintPluginsWelcomeMsg", "Display loaded plugins in the welcome message"), + LazyLoc.Localize("DalamudSettingsPrintPluginsWelcomeMsgHint", "Display loaded plugins in FFXIV chat when logging in with a character."), c => c.PrintPluginsWelcomeMsg, (v, c) => c.PrintPluginsWelcomeMsg = v), new SettingsEntry( - Loc.Localize("DalamudSettingsSystemMenu", "Dalamud buttons in system menu"), - Loc.Localize("DalamudSettingsSystemMenuMsgHint", "Add buttons for Dalamud plugins and settings to the system menu."), + LazyLoc.Localize("DalamudSettingsSystemMenu", "Dalamud buttons in system menu"), + LazyLoc.Localize("DalamudSettingsSystemMenuMsgHint", "Add buttons for Dalamud plugins and settings to the system menu."), c => c.DoButtonsSystemMenu, (v, c) => c.DoButtonsSystemMenu = v), new GapSettingsEntry(5), new SettingsEntry( - Loc.Localize("DalamudSettingDoMbCollect", "Anonymously upload market board data"), - Loc.Localize("DalamudSettingDoMbCollectHint", "Anonymously provide data about in-game economics to Universalis when browsing the market board. This data can't be tied to you in any way and everyone benefits!"), + LazyLoc.Localize("DalamudSettingDoMbCollect", "Anonymously upload market board data"), + LazyLoc.Localize("DalamudSettingDoMbCollectHint", "Anonymously provide data about in-game economics to Universalis when browsing the market board. This data can't be tied to you in any way and everyone benefits!"), c => c.IsMbCollect, (v, c) => c.IsMbCollect = v), - }; - - public override string Title => Loc.Localize("DalamudSettingsGeneral", "General"); + ]; } diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs index b6aa11db4..9b2c418b6 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabLook.cs @@ -7,54 +7,58 @@ using CheapLoc; using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Game; -using Dalamud.Game.Text; using Dalamud.Interface.Colors; using Dalamud.Interface.FontIdentifier; using Dalamud.Interface.GameFonts; using Dalamud.Interface.ImGuiFontChooserDialog; using Dalamud.Interface.ImGuiNotification.Internal; -using Dalamud.Interface.Internal.Windows.PluginInstaller; using Dalamud.Interface.Internal.Windows.Settings.Widgets; using Dalamud.Interface.ManagedFontAtlas.Internals; using Dalamud.Interface.Utility; using Dalamud.Utility; +using Dalamud.Utility.Internal; + using Serilog; namespace Dalamud.Interface.Internal.Windows.Settings.Tabs; [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")] -public class SettingsTabLook : SettingsTab +internal sealed class SettingsTabLook : SettingsTab { private static readonly (string, float)[] GlobalUiScalePresets = - { + [ ("80%##DalamudSettingsGlobalUiScaleReset96", 0.8f), ("100%##DalamudSettingsGlobalUiScaleReset12", 1f), ("117%##DalamudSettingsGlobalUiScaleReset14", 14 / 12f), ("150%##DalamudSettingsGlobalUiScaleReset18", 1.5f), ("200%##DalamudSettingsGlobalUiScaleReset24", 2f), ("300%##DalamudSettingsGlobalUiScaleReset36", 3f), - }; + ]; private float globalUiScale; private IFontSpec defaultFontSpec = null!; + public override string Title => Loc.Localize("DalamudSettingsVisual", "Look & Feel"); + + public override SettingsOpenKind Kind => SettingsOpenKind.LookAndFeel; + public override SettingsEntry[] Entries { get; } = [ new GapSettingsEntry(5, true), new ButtonSettingsEntry( - Loc.Localize("DalamudSettingsOpenStyleEditor", "Open Style Editor"), - Loc.Localize("DalamudSettingsStyleEditorHint", "Modify the look & feel of Dalamud windows."), + LazyLoc.Localize("DalamudSettingsOpenStyleEditor", "Open Style Editor"), + LazyLoc.Localize("DalamudSettingsStyleEditorHint", "Modify the look & feel of Dalamud windows."), () => Service.Get().OpenStyleEditor()), new ButtonSettingsEntry( - Loc.Localize("DalamudSettingsOpenNotificationEditor", "Modify Notification Position"), - Loc.Localize("DalamudSettingsNotificationEditorHint", "Choose where Dalamud notifications appear on the screen."), + LazyLoc.Localize("DalamudSettingsOpenNotificationEditor", "Modify Notification Position"), + LazyLoc.Localize("DalamudSettingsNotificationEditorHint", "Choose where Dalamud notifications appear on the screen."), () => Service.Get().StartPositionChooser()), new SettingsEntry( - Loc.Localize("DalamudSettingsUseDarkMode", "Use Windows immersive/dark mode"), - Loc.Localize("DalamudSettingsUseDarkModeHint", "This will cause the FFXIV window title bar to follow your preferred Windows color settings, and switch to dark mode if enabled."), + LazyLoc.Localize("DalamudSettingsUseDarkMode", "Use Windows immersive/dark mode"), + LazyLoc.Localize("DalamudSettingsUseDarkModeHint", "This will cause the FFXIV window title bar to follow your preferred Windows color settings, and switch to dark mode if enabled."), c => c.WindowIsImmersive, (v, c) => c.WindowIsImmersive = v, b => @@ -72,91 +76,87 @@ public class SettingsTabLook : SettingsTab new GapSettingsEntry(5, true), - new HintSettingsEntry(Loc.Localize("DalamudSettingToggleUiHideOptOutNote", "Plugins may independently opt out of the settings below.")), + new HintSettingsEntry(LazyLoc.Localize("DalamudSettingToggleUiHideOptOutNote", "Plugins may independently opt out of the settings below.")), new GapSettingsEntry(3), new SettingsEntry( - Loc.Localize("DalamudSettingToggleUiHide", "Hide plugin UI when the game UI is toggled off"), - Loc.Localize("DalamudSettingToggleUiHideHint", "Hide any open windows by plugins when toggling the game overlay."), + LazyLoc.Localize("DalamudSettingToggleUiHide", "Hide plugin UI when the game UI is toggled off"), + LazyLoc.Localize("DalamudSettingToggleUiHideHint", "Hide any open windows by plugins when toggling the game overlay."), c => c.ToggleUiHide, (v, c) => c.ToggleUiHide = v), new SettingsEntry( - Loc.Localize("DalamudSettingToggleUiHideDuringCutscenes", "Hide plugin UI during cutscenes"), - Loc.Localize("DalamudSettingToggleUiHideDuringCutscenesHint", "Hide any open windows by plugins during cutscenes."), + LazyLoc.Localize("DalamudSettingToggleUiHideDuringCutscenes", "Hide plugin UI during cutscenes"), + LazyLoc.Localize("DalamudSettingToggleUiHideDuringCutscenesHint", "Hide any open windows by plugins during cutscenes."), c => c.ToggleUiHideDuringCutscenes, (v, c) => c.ToggleUiHideDuringCutscenes = v), new SettingsEntry( - Loc.Localize("DalamudSettingToggleUiHideDuringGpose", "Hide plugin UI while gpose is active"), - Loc.Localize("DalamudSettingToggleUiHideDuringGposeHint", "Hide any open windows by plugins while gpose is active."), + LazyLoc.Localize("DalamudSettingToggleUiHideDuringGpose", "Hide plugin UI while gpose is active"), + LazyLoc.Localize("DalamudSettingToggleUiHideDuringGposeHint", "Hide any open windows by plugins while gpose is active."), c => c.ToggleUiHideDuringGpose, (v, c) => c.ToggleUiHideDuringGpose = v), new GapSettingsEntry(5, true), new SettingsEntry( - Loc.Localize("DalamudSettingToggleFocusManagement", "Use escape to close Dalamud windows"), - Loc.Localize("DalamudSettingToggleFocusManagementHint", "This will cause Dalamud windows to behave like in-game windows when pressing escape.\nThey will close one after another until all are closed. May not work for all plugins."), + LazyLoc.Localize("DalamudSettingToggleFocusManagement", "Use escape to close Dalamud windows"), + LazyLoc.Localize("DalamudSettingToggleFocusManagementHint", "This will cause Dalamud windows to behave like in-game windows when pressing escape.\nThey will close one after another until all are closed. May not work for all plugins."), c => c.IsFocusManagementEnabled, (v, c) => c.IsFocusManagementEnabled = v), // This is applied every frame in InterfaceManager::CheckViewportState() new SettingsEntry( - Loc.Localize("DalamudSettingToggleViewports", "Enable multi-monitor windows"), - Loc.Localize("DalamudSettingToggleViewportsHint", "This will allow you move plugin windows onto other monitors.\nWill only work in Borderless Window or Windowed mode."), + LazyLoc.Localize("DalamudSettingToggleViewports", "Enable multi-monitor windows"), + LazyLoc.Localize("DalamudSettingToggleViewportsHint", "This will allow you move plugin windows onto other monitors.\nWill only work in Borderless Window or Windowed mode."), c => !c.IsDisableViewport, (v, c) => c.IsDisableViewport = !v), new SettingsEntry( - Loc.Localize("DalamudSettingToggleDocking", "Enable window docking"), - Loc.Localize("DalamudSettingToggleDockingHint", "This will allow you to fuse and tab plugin windows."), + LazyLoc.Localize("DalamudSettingToggleDocking", "Enable window docking"), + LazyLoc.Localize("DalamudSettingToggleDockingHint", "This will allow you to fuse and tab plugin windows."), c => c.IsDocking, (v, c) => c.IsDocking = v), new SettingsEntry( - Loc.Localize( - "DalamudSettingEnablePluginUIAdditionalOptions", - "Add a button to the title bar of plugin windows to open additional options"), - Loc.Localize( - "DalamudSettingEnablePluginUIAdditionalOptionsHint", - "This will allow you to pin certain plugin windows, make them clickthrough or adjust their opacity.\nThis may not be supported by all of your plugins. Contact the plugin author if you want them to support this feature."), + LazyLoc.Localize("DalamudSettingEnablePluginUIAdditionalOptions", "Add a button to the title bar of plugin windows to open additional options"), + LazyLoc.Localize("DalamudSettingEnablePluginUIAdditionalOptionsHint", "This will allow you to pin certain plugin windows, make them clickthrough or adjust their opacity.\nThis may not be supported by all of your plugins. Contact the plugin author if you want them to support this feature."), c => c.EnablePluginUiAdditionalOptions, (v, c) => c.EnablePluginUiAdditionalOptions = v), new SettingsEntry( - Loc.Localize("DalamudSettingEnablePluginUISoundEffects", "Enable sound effects for plugin windows"), - Loc.Localize("DalamudSettingEnablePluginUISoundEffectsHint", "This will allow you to enable or disable sound effects generated by plugin user interfaces.\nThis is affected by your in-game `System Sounds` volume settings."), + LazyLoc.Localize("DalamudSettingEnablePluginUISoundEffects", "Enable sound effects for plugin windows"), + LazyLoc.Localize("DalamudSettingEnablePluginUISoundEffectsHint", "This will allow you to enable or disable sound effects generated by plugin user interfaces.\nThis is affected by your in-game `System Sounds` volume settings."), c => c.EnablePluginUISoundEffects, (v, c) => c.EnablePluginUISoundEffects = v), new SettingsEntry( - Loc.Localize("DalamudSettingToggleGamepadNavigation", "Control plugins via gamepad"), - Loc.Localize("DalamudSettingToggleGamepadNavigationHint", "This will allow you to toggle between game and plugin navigation via L1+L3.\nToggle the PluginInstaller window via R3 if ImGui navigation is enabled."), + LazyLoc.Localize("DalamudSettingToggleGamepadNavigation", "Control plugins via gamepad"), + LazyLoc.Localize("DalamudSettingToggleGamepadNavigationHint", "This will allow you to toggle between game and plugin navigation via L1+L3.\nToggle the PluginInstaller window via R3 if ImGui navigation is enabled."), c => c.IsGamepadNavigationEnabled, (v, c) => c.IsGamepadNavigationEnabled = v), new SettingsEntry( - Loc.Localize("DalamudSettingToggleTsm", "Show title screen menu"), - Loc.Localize("DalamudSettingToggleTsmHint", "This will allow you to access certain Dalamud and Plugin functionality from the title screen.\nDisabling this will also hide the Dalamud version text on the title screen."), + LazyLoc.Localize("DalamudSettingToggleTsm", "Show title screen menu"), + LazyLoc.Localize("DalamudSettingToggleTsmHint", "This will allow you to access certain Dalamud and Plugin functionality from the title screen.\nDisabling this will also hide the Dalamud version text on the title screen."), c => c.ShowTsm, (v, c) => c.ShowTsm = v), new SettingsEntry( - Loc.Localize("DalamudSettingInstallerOpenDefault", "Open the Plugin Installer to the \"Installed Plugins\" tab by default"), - Loc.Localize("DalamudSettingInstallerOpenDefaultHint", "This will allow you to open the Plugin Installer to the \"Installed Plugins\" tab by default, instead of the \"Available Plugins\" tab."), + LazyLoc.Localize("DalamudSettingInstallerOpenDefault", "Open the Plugin Installer to the \"Installed Plugins\" tab by default"), + LazyLoc.Localize("DalamudSettingInstallerOpenDefaultHint", "This will allow you to open the Plugin Installer to the \"Installed Plugins\" tab by default, instead of the \"Available Plugins\" tab."), c => c.PluginInstallerOpen == PluginInstallerOpenKind.InstalledPlugins, (v, c) => c.PluginInstallerOpen = v ? PluginInstallerOpenKind.InstalledPlugins : PluginInstallerOpenKind.AllPlugins), new SettingsEntry( - Loc.Localize("DalamudSettingReducedMotion", "Reduce motions"), - Loc.Localize("DalamudSettingReducedMotionHint", "This will suppress certain animations from Dalamud, such as the notification popup."), + LazyLoc.Localize("DalamudSettingReducedMotion", "Reduce motions"), + LazyLoc.Localize("DalamudSettingReducedMotionHint", "This will suppress certain animations from Dalamud, such as the notification popup."), c => c.ReduceMotions ?? false, (v, c) => c.ReduceMotions = v), new SettingsEntry( - Loc.Localize("DalamudSettingImeStateIndicatorOpacity", "IME State Indicator Opacity (CJK only)"), - Loc.Localize("DalamudSettingImeStateIndicatorOpacityHint", "When any of CJK IMEs is in use, the state of IME will be shown with the opacity specified here."), + LazyLoc.Localize("DalamudSettingImeStateIndicatorOpacity", "IME State Indicator Opacity (CJK only)"), + LazyLoc.Localize("DalamudSettingImeStateIndicatorOpacityHint", "When any of CJK IMEs is in use, the state of IME will be shown with the opacity specified here."), c => c.ImeStateIndicatorOpacity, (v, c) => c.ImeStateIndicatorOpacity = v) { @@ -176,8 +176,6 @@ public class SettingsTabLook : SettingsTab } ]; - public override string Title => Loc.Localize("DalamudSettingsVisual", "Look & Feel"); - public override void Draw() { var interfaceManager = Service.Get(); @@ -212,7 +210,7 @@ public class SettingsTabLook : SettingsTab var p = stackalloc byte[len]; Encoding.UTF8.GetBytes(buildingFonts, new(p, len)); ImGui.Text( - new ReadOnlySpan(p, len)[..((len + ((Environment.TickCount / 200) % 3)) - 2)]); + new ReadOnlySpan(p, len)[..(len + (Environment.TickCount / 200 % 3) - 2)]); } } diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/ButtonSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/ButtonSettingsEntry.cs index b53411c7c..7d72464ac 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/ButtonSettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/ButtonSettingsEntry.cs @@ -1,18 +1,18 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Colors; -using Dalamud.Interface.Utility; +using Dalamud.Utility.Internal; namespace Dalamud.Interface.Internal.Windows.Settings.Widgets; [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")] -public class ButtonSettingsEntry : SettingsEntry +internal sealed class ButtonSettingsEntry : SettingsEntry { - private readonly string description; + private readonly LazyLoc description; private readonly Action runs; - public ButtonSettingsEntry(string name, string description, Action runs) + public ButtonSettingsEntry(LazyLoc name, LazyLoc description, Action runs) { this.description = description; this.runs = runs; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs index 2e569b565..dd17a946a 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs @@ -6,6 +6,7 @@ using System.Numerics; using System.Threading.Tasks; using CheapLoc; + using Dalamud.Bindings.ImGui; using Dalamud.Configuration; using Dalamud.Configuration.Internal; @@ -15,13 +16,14 @@ using Dalamud.Interface.ImGuiFileDialog; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin.Internal; +using Dalamud.Utility.Internal; namespace Dalamud.Interface.Internal.Windows.Settings.Widgets; [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")] -public class DevPluginsSettingsEntry : SettingsEntry +internal sealed class DevPluginsSettingsEntry : SettingsEntry { - private List devPluginLocations = new(); + private List devPluginLocations = []; private bool devPluginLocationsChanged; private string devPluginTempLocation = string.Empty; private string devPluginLocationAddError = string.Empty; @@ -29,25 +31,25 @@ public class DevPluginsSettingsEntry : SettingsEntry public DevPluginsSettingsEntry() { - this.Name = Loc.Localize("DalamudSettingsDevPluginLocation", "Dev Plugin Locations"); + this.Name = LazyLoc.Localize("DalamudSettingsDevPluginLocation", "Dev Plugin Locations"); } public override void OnClose() { this.devPluginLocations = - Service.Get().DevPluginLoadLocations.Select(x => x.Clone()).ToList(); + [.. Service.Get().DevPluginLoadLocations.Select(x => x.Clone())]; } public override void Load() { this.devPluginLocations = - Service.Get().DevPluginLoadLocations.Select(x => x.Clone()).ToList(); + [.. Service.Get().DevPluginLoadLocations.Select(x => x.Clone())]; this.devPluginLocationsChanged = false; } public override void Save() { - Service.Get().DevPluginLoadLocations = this.devPluginLocations.Select(x => x.Clone()).ToList(); + Service.Get().DevPluginLoadLocations = [.. this.devPluginLocations.Select(x => x.Clone())]; if (this.devPluginLocationsChanged) { @@ -59,7 +61,9 @@ public class DevPluginsSettingsEntry : SettingsEntry public override void Draw() { using var id = ImRaii.PushId("devPluginLocation"u8); + ImGui.Text(this.Name); + if (this.devPluginLocationsChanged) { using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen)) diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/EnumSettingsEntry{T}.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/EnumSettingsEntry{T}.cs index 03123ad95..8fb91940e 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/EnumSettingsEntry{T}.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/EnumSettingsEntry{T}.cs @@ -1,12 +1,15 @@ -using System.Diagnostics; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using CheapLoc; + using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; +using Dalamud.Utility.Internal; namespace Dalamud.Interface.Internal.Windows.Settings.Widgets; @@ -23,8 +26,8 @@ internal sealed class EnumSettingsEntry : SettingsEntry private T valueBacking; public EnumSettingsEntry( - string name, - string description, + LazyLoc name, + LazyLoc description, LoadSettingDelegate load, SaveSettingDelegate save, Action? change = null, @@ -61,7 +64,7 @@ internal sealed class EnumSettingsEntry : SettingsEntry } } - public string Description { get; } + public LazyLoc Description { get; } public Action>? CustomDraw { get; init; } @@ -79,7 +82,10 @@ internal sealed class EnumSettingsEntry : SettingsEntry public override void Draw() { - Debug.Assert(this.Name != null, "this.Name != null"); + var name = this.Name.ToString(); + var description = this.Description.ToString(); + + Debug.Assert(!string.IsNullOrWhiteSpace(name), "Name is empty"); if (this.CustomDraw is not null) { @@ -87,7 +93,7 @@ internal sealed class EnumSettingsEntry : SettingsEntry } else { - ImGui.TextWrapped(this.Name); + ImGui.TextWrapped(name); var idx = this.valueBacking; var values = Enum.GetValues(); @@ -117,13 +123,14 @@ internal sealed class EnumSettingsEntry : SettingsEntry using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudGrey)) { var desc = this.FriendlyEnumDescriptionGetter(this.valueBacking); + if (!string.IsNullOrWhiteSpace(desc)) { ImGui.TextWrapped(desc); ImGuiHelpers.ScaledDummy(2); } - ImGui.TextWrapped(this.Description); + ImGui.TextWrapped(description); } if (this.CheckValidity != null) diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/GapSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/GapSettingsEntry.cs index 88982b825..c220ea684 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/GapSettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/GapSettingsEntry.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Utility; @@ -6,7 +6,7 @@ using Dalamud.Interface.Utility; namespace Dalamud.Interface.Internal.Windows.Settings.Widgets; [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")] -public sealed class GapSettingsEntry : SettingsEntry +internal sealed class GapSettingsEntry : SettingsEntry { private readonly float size; private readonly bool hr; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/HintSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/HintSettingsEntry.cs index 1937adc57..7159effdf 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/HintSettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/HintSettingsEntry.cs @@ -3,16 +3,17 @@ using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Colors; +using Dalamud.Utility.Internal; namespace Dalamud.Interface.Internal.Windows.Settings.Widgets; [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")] -public class HintSettingsEntry : SettingsEntry +internal sealed class HintSettingsEntry : SettingsEntry { - private readonly string text; + private readonly LazyLoc text; private readonly Vector4 color; - public HintSettingsEntry(string text, Vector4? color = null) + public HintSettingsEntry(LazyLoc text, Vector4? color = null) { this.text = text; this.color = color ?? ImGuiColors.DalamudGrey; diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/LanguageChooserSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/LanguageChooserSettingsEntry.cs index 4e328720b..9459413df 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/LanguageChooserSettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/LanguageChooserSettingsEntry.cs @@ -3,15 +3,16 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using CheapLoc; + using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; -using Dalamud.Interface.Utility; +using Dalamud.Utility.Internal; namespace Dalamud.Interface.Internal.Windows.Settings.Widgets; [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")] -public sealed class LanguageChooserSettingsEntry : SettingsEntry +internal sealed class LanguageChooserSettingsEntry : SettingsEntry { private readonly string[] languages; private readonly string[] locLanguages; @@ -20,9 +21,9 @@ public sealed class LanguageChooserSettingsEntry : SettingsEntry public LanguageChooserSettingsEntry() { - this.languages = Localization.ApplicableLangCodes.Prepend("en").ToArray(); + this.languages = [.. Localization.ApplicableLangCodes.Prepend("en")]; - this.Name = Loc.Localize("DalamudSettingsLanguage", "Language"); + this.Name = LazyLoc.Localize("DalamudSettingsLanguage", "Language"); this.IsValid = true; this.IsVisible = true; @@ -46,7 +47,7 @@ public sealed class LanguageChooserSettingsEntry : SettingsEntry } } - this.locLanguages = locLanguagesList.ToArray(); + this.locLanguages = [.. locLanguagesList]; } catch (Exception) { diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs index 2349d0d7a..e901550da 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/SettingsEntry{T}.cs @@ -1,12 +1,12 @@ -using System.Diagnostics; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using Dalamud.Bindings.ImGui; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; -using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; +using Dalamud.Utility.Internal; namespace Dalamud.Interface.Internal.Windows.Settings.Widgets; @@ -20,8 +20,8 @@ internal sealed class SettingsEntry : SettingsEntry private object? valueBacking; public SettingsEntry( - string name, - string description, + LazyLoc name, + LazyLoc description, LoadSettingDelegate load, SaveSettingDelegate save, Action? change = null, @@ -55,7 +55,7 @@ internal sealed class SettingsEntry : SettingsEntry } } - public string Description { get; } + public LazyLoc Description { get; } public Action>? CustomDraw { get; init; } @@ -69,7 +69,10 @@ internal sealed class SettingsEntry : SettingsEntry public override void Draw() { - Debug.Assert(this.Name != null, "this.Name != null"); + var name = this.Name.ToString(); + var description = this.Description.ToString(); + + Debug.Assert(!string.IsNullOrWhiteSpace(name), "Name is empty"); var type = typeof(T); @@ -79,7 +82,7 @@ internal sealed class SettingsEntry : SettingsEntry } else if (type == typeof(DirectoryInfo)) { - ImGui.TextWrapped(this.Name); + ImGui.TextWrapped(name); var value = this.Value as DirectoryInfo; var nativeBuffer = value?.FullName ?? string.Empty; @@ -91,7 +94,7 @@ internal sealed class SettingsEntry : SettingsEntry } else if (type == typeof(string)) { - ImGui.TextWrapped(this.Name); + ImGui.TextWrapped(name); var nativeBuffer = this.Value as string ?? string.Empty; @@ -104,16 +107,19 @@ internal sealed class SettingsEntry : SettingsEntry { var nativeValue = this.Value as bool? ?? false; - if (ImGui.Checkbox($"{this.Name}###{this.Id.ToString()}", ref nativeValue)) + if (ImGui.Checkbox($"{name}###{this.Id.ToString()}", ref nativeValue)) { this.valueBacking = nativeValue; this.change?.Invoke(this.Value); } } - using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudGrey)) + if (!string.IsNullOrWhiteSpace(description)) { - ImGui.TextWrapped(this.Description); + using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudGrey)) + { + ImGui.TextWrapped(this.Description); + } } if (this.CheckValidity != null) diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/ThirdRepoSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/ThirdRepoSettingsEntry.cs index cfcf6b3ac..5737b44db 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/ThirdRepoSettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/ThirdRepoSettingsEntry.cs @@ -18,9 +18,9 @@ using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.Settings.Widgets; [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Internals")] -public class ThirdRepoSettingsEntry : SettingsEntry +internal class ThirdRepoSettingsEntry : SettingsEntry { - private List thirdRepoList = new(); + private List thirdRepoList = []; private bool thirdRepoListChanged; private string thirdRepoTempUrl = string.Empty; private string thirdRepoAddError = string.Empty; @@ -34,20 +34,20 @@ public class ThirdRepoSettingsEntry : SettingsEntry public override void OnClose() { this.thirdRepoList = - Service.Get().ThirdRepoList.Select(x => x.Clone()).ToList(); + [.. Service.Get().ThirdRepoList.Select(x => x.Clone())]; } public override void Load() { this.thirdRepoList = - Service.Get().ThirdRepoList.Select(x => x.Clone()).ToList(); + [.. Service.Get().ThirdRepoList.Select(x => x.Clone())]; this.thirdRepoListChanged = false; } public override void Save() { Service.Get().ThirdRepoList = - this.thirdRepoList.Select(x => x.Clone()).ToList(); + [.. this.thirdRepoList.Select(x => x.Clone())]; if (this.thirdRepoListChanged) { diff --git a/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs b/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs index 09106fd0e..f9e8022a1 100644 --- a/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs +++ b/Dalamud/Interface/Internal/Windows/StyleEditor/StyleEditorWindow.cs @@ -254,7 +254,8 @@ public class StyleEditorWindow : Window ImGui.Text("Alignment"u8); changes |= ImGui.SliderFloat2("WindowTitleAlign", ref style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); var windowMenuButtonPosition = (int)style.WindowMenuButtonPosition + 1; - if (ImGui.Combo("WindowMenuButtonPosition"u8, ref windowMenuButtonPosition, ["None", "Left", "Right"])) { + if (ImGui.Combo("WindowMenuButtonPosition"u8, ref windowMenuButtonPosition, ["None", "Left", "Right"])) + { style.WindowMenuButtonPosition = (ImGuiDir)(windowMenuButtonPosition - 1); changes = true; } diff --git a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs index cedd260a6..e3eb22a04 100644 --- a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs +++ b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs @@ -477,7 +477,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable var textNode = addon->GetTextNodeById(3); // look and feel init. should be harmless to set. - textNode->TextFlags |= (byte)TextFlags.MultiLine; + textNode->TextFlags |= TextFlags.MultiLine; textNode->AlignmentType = AlignmentType.TopLeft; var containsDalamudVersionString = textNode->OriginalTextPointer.Value == textNode->NodeText.StringPtr.Value; diff --git a/Dalamud/Interface/Textures/TextureWraps/Internal/ViewportTextureWrap.cs b/Dalamud/Interface/Textures/TextureWraps/Internal/ViewportTextureWrap.cs index 2247fc781..3e0f31eca 100644 --- a/Dalamud/Interface/Textures/TextureWraps/Internal/ViewportTextureWrap.cs +++ b/Dalamud/Interface/Textures/TextureWraps/Internal/ViewportTextureWrap.cs @@ -56,7 +56,7 @@ internal sealed class ViewportTextureWrap : IDalamudTextureWrap, IDeferredDispos get { var t = (nint)this.srv.Get(); - return t == nint.Zero ? Service.Get().Empty4X4.Handle : ImTextureID.Null; + return t == nint.Zero ? Service.Get().Empty4X4.Handle : new ImTextureID(this.srv.Get()); } } diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs index 355b5d571..1ea0d9f2f 100644 --- a/Dalamud/Interface/UiBuilder.cs +++ b/Dalamud/Interface/UiBuilder.cs @@ -123,6 +123,34 @@ public interface IUiBuilder /// IFontSpec DefaultFontSpec { get; } + /// + /// Gets the default Dalamud font size in points. + /// + public float FontDefaultSizePt { get; } + + /// + /// Gets the default Dalamud font size in pixels. + /// + public float FontDefaultSizePx { get; } + + /// + /// Gets the default Dalamud font - supporting all game languages and icons.
+ /// Accessing this static property outside of is dangerous and not supported. + ///
+ public ImFontPtr FontDefault { get; } + + /// + /// Gets the default Dalamud icon font based on FontAwesome 5 Free solid.
+ /// Accessing this static property outside of is dangerous and not supported. + ///
+ public ImFontPtr FontIcon { get; } + + /// + /// Gets the default Dalamud monospaced font based on Inconsolata Regular.
+ /// Accessing this static property outside of is dangerous and not supported. + ///
+ public ImFontPtr FontMono { get; } + /// /// Gets the game's active Direct3D device. /// @@ -381,6 +409,21 @@ public sealed class UiBuilder : IDisposable, IUiBuilder ///
public IFontSpec DefaultFontSpec => Service.Get().DefaultFontSpec; + /// + public float FontDefaultSizePt => Service.Get().DefaultFontSpec.SizePt; + + /// + public float FontDefaultSizePx => Service.Get().DefaultFontSpec.SizePx; + + /// + public ImFontPtr FontDefault => InterfaceManager.DefaultFont; + + /// + public ImFontPtr FontIcon => InterfaceManager.IconFont; + + /// + public ImFontPtr FontMono => InterfaceManager.MonoFont; + /// /// Gets the handle to the default Dalamud font - supporting all game languages and icons. /// @@ -533,15 +576,6 @@ public sealed class UiBuilder : IDisposable, IUiBuilder ///
public bool ShouldUseReducedMotion => Service.Get().ReduceMotions ?? false; - private void OnDefaultStyleChanged() - => this.DefaultStyleChanged.InvokeSafely(); - - private void OnDefaultGlobalScaleChanged() - => this.DefaultGlobalScaleChanged.InvokeSafely(); - - private void OnDefaultFontChanged() - => this.DefaultFontChanged.InvokeSafely(); - /// /// Gets or sets a value indicating whether statistics about UI draw time should be collected. /// @@ -798,6 +832,15 @@ public sealed class UiBuilder : IDisposable, IUiBuilder this.ResizeBuffers?.InvokeSafely(); } + private void OnDefaultStyleChanged() + => this.DefaultStyleChanged.InvokeSafely(); + + private void OnDefaultGlobalScaleChanged() + => this.DefaultGlobalScaleChanged.InvokeSafely(); + + private void OnDefaultFontChanged() + => this.DefaultFontChanged.InvokeSafely(); + private class FontHandleWrapper : IFontHandle { private IFontHandle? wrapped; diff --git a/Dalamud/Interface/Utility/ImGuiHelpers.cs b/Dalamud/Interface/Utility/ImGuiHelpers.cs index 98159c1bc..b8e7d5fe3 100644 --- a/Dalamud/Interface/Utility/ImGuiHelpers.cs +++ b/Dalamud/Interface/Utility/ImGuiHelpers.cs @@ -203,8 +203,8 @@ public static partial class ImGuiHelpers ImGui.SetClipboardText(textCopy.IsNull ? text.Span : textCopy.Span); } - text.Dispose(); - textCopy.Dispose(); + text.Recycle(); + textCopy.Recycle(); } /// Draws a SeString. diff --git a/Dalamud/Interface/Utility/Raii/EndObjects.cs b/Dalamud/Interface/Utility/Raii/EndObjects.cs index f84844dda..80122360c 100644 --- a/Dalamud/Interface/Utility/Raii/EndObjects.cs +++ b/Dalamud/Interface/Utility/Raii/EndObjects.cs @@ -40,6 +40,9 @@ public static partial class ImRaii public static IEndObject PopupModal(ImU8String id) => new EndConditionally(ImGui.EndPopup, ImGui.BeginPopupModal(id)); + public static IEndObject PopupModal(ImU8String id, ImGuiWindowFlags flags) + => new EndConditionally(ImGui.EndPopup, ImGui.BeginPopupModal(id, flags)); + public static IEndObject PopupModal(ImU8String id, ref bool open) => new EndConditionally(ImGui.EndPopup, ImGui.BeginPopupModal(id, ref open)); @@ -242,7 +245,7 @@ public static partial class ImRaii // Use end-function regardless of success. // Used by Child, Group and Tooltip. - private struct EndUnconditionally : IEndObject + public struct EndUnconditionally : IEndObject { private Action EndAction { get; } @@ -268,7 +271,7 @@ public static partial class ImRaii } // Use end-function only on success. - private struct EndConditionally : IEndObject + public struct EndConditionally : IEndObject { public EndConditionally(Action endAction, bool success) { diff --git a/Dalamud/Interface/Windowing/Window.cs b/Dalamud/Interface/Windowing/Window.cs index d302552f5..e90e38119 100644 --- a/Dalamud/Interface/Windowing/Window.cs +++ b/Dalamud/Interface/Windowing/Window.cs @@ -55,6 +55,9 @@ public abstract class Window private Vector2 fadeOutSize = Vector2.Zero; private Vector2 fadeOutOrigin = Vector2.Zero; + private bool hasError = false; + private Exception? lastError; + /// /// Initializes a new instance of the class. /// @@ -223,6 +226,16 @@ public abstract class Window ///
public bool AllowClickthrough { get; set; } = true; + /// + /// Gets a value indicating whether this window is pinned. + /// + public bool IsPinned => this.internalIsPinned; + + /// + /// Gets a value indicating whether this window is click-through. + /// + public bool IsClickthrough => this.internalIsClickthrough; + /// /// Gets or sets a list of available title bar buttons. /// @@ -412,8 +425,17 @@ public abstract class Window UIGlobals.PlaySoundEffect(this.OnOpenSfxId); } - this.PreDraw(); - this.ApplyConditionals(); + var isErrorStylePushed = false; + if (!this.hasError) + { + this.PreDraw(); + this.ApplyConditionals(); + } + else + { + Style.StyleModelV1.DalamudStandard.Push(); + isErrorStylePushed = true; + } if (this.ForceMainWindow) ImGuiHelpers.ForceNextWindowMainViewport(); @@ -435,10 +457,22 @@ 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)) { @@ -458,14 +492,24 @@ public abstract class Window this.presetDirty = true; } - // Draw the actual window contents - try + if (this.hasError) { - this.Draw(); + this.DrawErrorMessage(); } - catch (Exception ex) + else { - Log.Error(ex, "Error during Draw(): {WindowName}", this.WindowName); + // Draw the actual window contents + try + { + this.Draw(); + } + catch (Exception ex) + { + Log.Error(ex, "Error during Draw(): {WindowName}", this.WindowName); + + this.hasError = true; + this.lastError = ex; + } } } @@ -523,7 +567,7 @@ public abstract class Window if (ImGui.SliderFloat(Loc.Localize("WindowSystemContextActionAlpha", "Opacity"), ref alpha, 20f, 100f)) { - this.internalAlpha = alpha / 100f; + this.internalAlpha = Math.Clamp(alpha / 100f, 0.2f, 1f); this.presetDirty = true; } @@ -647,7 +691,17 @@ public abstract class Window Task.FromResult(tex)); } - this.PostDraw(); + if (!this.hasError) + { + this.PostDraw(); + } + else + { + if (isErrorStylePushed) + { + Style.StyleModelV1.DalamudStandard.Pop(); + } + } this.PostHandlePreset(persistence); @@ -793,7 +847,7 @@ public abstract class Window hovered = true; // We can't use ImGui native functions here, because they don't work with clickthrough - if ((global::Windows.Win32.PInvoke.GetKeyState((int)VirtualKey.LBUTTON) & 0x8000) != 0) + if ((Windows.Win32.PInvoke.GetKeyState((int)VirtualKey.LBUTTON) & 0x8000) != 0) { held = true; pressed = true; @@ -871,6 +925,52 @@ public abstract class Window ImGui.End(); } + 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.")); + + ImGuiHelpers.ScaledDummy(5); + + if (ImGui.Button(Loc.Localize("WindowSystemErrorRecoverButton", "Attempt to retry"))) + { + this.hasError = false; + this.lastError = null; + } + + ImGui.SameLine(); + + if (ImGui.Button(Loc.Localize("WindowSystemErrorClose", "Close Window"))) + { + this.IsOpen = false; + this.hasError = false; + this.lastError = null; + } + + ImGuiHelpers.ScaledDummy(10); + + if (this.lastError != null) + { + using var child = ImRaii.Child("##ErrorDetails", new Vector2(0, 200 * ImGuiHelpers.GlobalScale), true); + using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudGrey)) + { + ImGui.TextWrapped(Loc.Localize("WindowSystemErrorDetails", "Error Details:")); + ImGui.Separator(); + ImGui.TextWrapped(this.lastError.ToString()); + } + + var childWindowSize = ImGui.GetWindowSize(); + var copyText = Loc.Localize("WindowSystemErrorCopy", "Copy"); + var buttonWidth = ImGuiComponents.GetIconButtonWithTextWidth(FontAwesomeIcon.Copy, copyText); + ImGui.SetCursorPos(new Vector2(childWindowSize.X - buttonWidth - ImGui.GetStyle().FramePadding.X, + ImGui.GetStyle().FramePadding.Y)); + if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Copy, copyText)) + { + ImGui.SetClipboardText(this.lastError.ToString()); + } + } + } + /// /// Structure detailing the size constraints of a window. /// diff --git a/Dalamud/Interface/Windowing/WindowSystem.cs b/Dalamud/Interface/Windowing/WindowSystem.cs index 87bd199a1..d6e9649bb 100644 --- a/Dalamud/Interface/Windowing/WindowSystem.cs +++ b/Dalamud/Interface/Windowing/WindowSystem.cs @@ -60,6 +60,12 @@ public class WindowSystem /// public string? Namespace { get; set; } + /// + /// 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. + /// + internal static bool ShouldInhibitAtkCloseEvents { get; set; } + /// /// Add a window to this . /// The window system doesn't own your window, it just renders it @@ -130,7 +136,7 @@ public class WindowSystem window.DrawInternal(flags, persistence); } - var focusedWindow = this.windows.FirstOrDefault(window => window.IsFocused && window.RespectCloseHotkey); + var focusedWindow = this.windows.FirstOrDefault(window => window.IsFocused); this.HasAnyFocus = focusedWindow != default; if (this.HasAnyFocus) @@ -155,6 +161,11 @@ 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/ServiceContainer.cs b/Dalamud/IoC/Internal/ServiceContainer.cs index 6745155f6..6383b6b11 100644 --- a/Dalamud/IoC/Internal/ServiceContainer.cs +++ b/Dalamud/IoC/Internal/ServiceContainer.cs @@ -18,7 +18,7 @@ namespace Dalamud.IoC.Internal; /// Dalamud services are constructed via Service{T}.ConstructObject at the moment. /// [ServiceManager.ProvidedService] -internal class ServiceContainer : IServiceProvider, IServiceType +internal class ServiceContainer : IServiceType { private static readonly ModuleLog Log = new("SERVICECONTAINER"); @@ -112,27 +112,15 @@ internal class ServiceContainer : IServiceProvider, IServiceType errorStep = "property injection"; await this.InjectProperties(instance, scopedObjects, scope); + // Invoke ctor from a separate thread (LongRunning will spawn a new one) + // so that it does not count towards thread pool active threads cap. + // Plugin ctor can block to wait for Tasks, as we currently do not support asynchronous plugin init. errorStep = "ctor invocation"; - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var thr = new Thread( - () => - { - try - { - ctor.Invoke(instance, resolvedParams); - } - catch (Exception e) - { - tcs.SetException(e); - return; - } - - tcs.SetResult(); - }); - - thr.Start(); - await tcs.Task.ConfigureAwait(false); - thr.Join(); + await Task.Factory.StartNew( + () => ctor.Invoke(instance, resolvedParams), + CancellationToken.None, + TaskCreationOptions.LongRunning, + TaskScheduler.Default).ConfigureAwait(false); return instance; } @@ -172,10 +160,21 @@ internal class ServiceContainer : IServiceProvider, IServiceType /// An implementation of a service scope. public IServiceScope GetScope() => new ServiceScopeImpl(this); - /// - object? IServiceProvider.GetService(Type serviceType) => this.GetSingletonService(serviceType); - - private async Task GetService(Type serviceType, ServiceScopeImpl? scope, object[] scopedObjects) + /// + /// Resolves and returns an instance of the specified service type, using either singleton or scoped lifetime as + /// appropriate. + /// + /// The type of the service to resolve. This must be a concrete or interface type registered with the service + /// manager. + /// The scope within which to create scoped services. Required if the requested service type is registered as + /// scoped; otherwise, can be null. + /// An array of objects available for scoped resolution. Used to locate or create scoped service instances when + /// applicable. + /// An instance of the requested service type. Returns a singleton instance if available, a scoped instance if + /// required, or an object from the provided scoped objects if it matches the service type. + /// Thrown if a scoped service is requested but no scope is provided, or if the requested service type cannot be + /// resolved from the scoped objects. + public async Task GetService(Type serviceType, ServiceScopeImpl? scope, object[] scopedObjects) { if (this.interfaceToTypeMap.TryGetValue(serviceType, out var implementingType)) serviceType = implementingType; diff --git a/Dalamud/IoC/Internal/ServiceScope.cs b/Dalamud/IoC/Internal/ServiceScope.cs index 98209eeb7..c0c4e0b08 100644 --- a/Dalamud/IoC/Internal/ServiceScope.cs +++ b/Dalamud/IoC/Internal/ServiceScope.cs @@ -1,4 +1,4 @@ -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -12,7 +12,7 @@ namespace Dalamud.IoC.Internal; /// /// Container enabling the creation of scoped services. /// -internal interface IServiceScope : IAsyncDisposable +internal interface IServiceScope : IServiceProvider, IAsyncDisposable { /// /// Register objects that may be injected to scoped services, @@ -57,6 +57,12 @@ internal class ServiceScopeImpl : IServiceScope /// The container this scope will use to create services. public ServiceScopeImpl(ServiceContainer container) => this.container = container; + /// + public object? GetService(Type serviceType) + { + return this.container.GetService(serviceType, this, []).ConfigureAwait(false).GetAwaiter().GetResult(); + } + /// public void RegisterPrivateScopes(params object[] scopes) { diff --git a/Dalamud/Localization.cs b/Dalamud/Localization.cs index 1e72c3075..0a7086e73 100644 --- a/Dalamud/Localization.cs +++ b/Dalamud/Localization.cs @@ -112,11 +112,14 @@ public class Localization : IServiceType } /// - /// Set up the UI language with "fallbacks"(original English text). + /// Set up the UI language with "fallbacks" (original English text). /// public void SetupWithFallbacks() { this.DalamudLanguageCultureInfo = CultureInfo.InvariantCulture; + + Loc.SetupWithFallbacks(this.assembly); + foreach (var d in Delegate.EnumerateInvocationList(this.LocalizationChanged)) { try @@ -128,8 +131,6 @@ public class Localization : IServiceType Log.Error(ex, "Exception during raise of {handler}", d.Method); } } - - Loc.SetupWithFallbacks(this.assembly); } /// @@ -145,6 +146,17 @@ public class Localization : IServiceType } this.DalamudLanguageCultureInfo = GetCultureInfoFromLangCode(langCode); + + try + { + Loc.Setup(this.ReadLocData(langCode), this.assembly); + } + catch (Exception ex) + { + Log.Error(ex, "Could not load loc {0}. Setting up fallbacks.", langCode); + this.SetupWithFallbacks(); + } + foreach (var d in Delegate.EnumerateInvocationList(this.LocalizationChanged)) { try @@ -156,16 +168,6 @@ public class Localization : IServiceType Log.Error(ex, "Exception during raise of {handler}", d.Method); } } - - try - { - Loc.Setup(this.ReadLocData(langCode), this.assembly); - } - catch (Exception ex) - { - Log.Error(ex, "Could not load loc {0}. Setting up fallbacks.", langCode); - this.SetupWithFallbacks(); - } } /// diff --git a/Dalamud/Memory/MemoryHelper.cs b/Dalamud/Memory/MemoryHelper.cs index 0f557c3f6..2eae1be6d 100644 --- a/Dalamud/Memory/MemoryHelper.cs +++ b/Dalamud/Memory/MemoryHelper.cs @@ -8,13 +8,12 @@ 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; -using LPayloadType = Lumina.Text.Payloads.PayloadType; -using LSeString = Lumina.Text.SeString; - // Heavily inspired from Reloaded (https://github.com/Reloaded-Project/Reloaded.Memory) namespace Dalamud.Memory; @@ -414,7 +413,7 @@ public static unsafe class MemoryHelper containsNonRepresentedPayload = false; while (*pin != 0 && maxLength > 0) { - if (*pin != LSeString.StartByte) + if (*pin != ReadOnlySeString.Stx) { var len = *pin switch { @@ -439,7 +438,7 @@ public static unsafe class MemoryHelper --maxLength; // Payload type - var payloadType = (LPayloadType)(*pin++); + var payloadType = (MacroCode)(*pin++); // Payload length if (!ReadIntExpression(ref pin, ref maxLength, out var expressionLength)) @@ -450,19 +449,19 @@ public static unsafe class MemoryHelper maxLength -= unchecked((int)expressionLength); // End byte - if (*pin++ != LSeString.EndByte) + if (*pin++ != ReadOnlySeString.Etx) break; --maxLength; switch (payloadType) { - case LPayloadType.NewLine: + case MacroCode.NewLine: sb.AppendLine(); break; - case LPayloadType.Hyphen: + case MacroCode.Hyphen: sb.Append('–'); break; - case LPayloadType.SoftHyphen: + case MacroCode.SoftHyphen: sb.Append('\u00AD'); break; default: diff --git a/Dalamud/Plugin/ActivePluginsChangedEventArgs.cs b/Dalamud/Plugin/ActivePluginsChangedEventArgs.cs index 0c857be79..7a3991dcd 100644 --- a/Dalamud/Plugin/ActivePluginsChangedEventArgs.cs +++ b/Dalamud/Plugin/ActivePluginsChangedEventArgs.cs @@ -2,10 +2,8 @@ using System.Collections.Generic; namespace Dalamud.Plugin; -/// -/// Contains data about changes to the list of active plugins. -/// -public class ActivePluginsChangedEventArgs : EventArgs +/// +public class ActivePluginsChangedEventArgs : EventArgs, IActivePluginsChangedEventArgs { /// /// Initializes a new instance of the class @@ -19,13 +17,9 @@ public class ActivePluginsChangedEventArgs : EventArgs this.AffectedInternalNames = affectedInternalNames; } - /// - /// Gets the invalidation kind that caused this event to be fired. - /// + /// public PluginListInvalidationKind Kind { get; } - /// - /// Gets the InternalNames of affected plugins. - /// + /// public IEnumerable AffectedInternalNames { get; } } diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs index 25c998a42..6fd9064b6 100644 --- a/Dalamud/Plugin/DalamudPluginInterface.cs +++ b/Dalamud/Plugin/DalamudPluginInterface.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.Loader; using System.Threading.Tasks; using Dalamud.Configuration; @@ -16,6 +17,7 @@ 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; @@ -25,6 +27,7 @@ using Dalamud.Plugin.Internal.Types.Manifest; using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Ipc.Exceptions; using Dalamud.Plugin.Ipc.Internal; +using Dalamud.Plugin.Services; using Serilog; @@ -83,126 +86,73 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa configuration.DalamudConfigurationSaved += this.OnDalamudConfigurationSaved; } - /// - /// Event that gets fired when loc is changed - /// + /// public event IDalamudPluginInterface.LanguageChangedDelegate? LanguageChanged; - /// - /// Event that is fired when the active list of plugins is changed. - /// + /// public event IDalamudPluginInterface.ActivePluginsChangedDelegate? ActivePluginsChanged; - /// - /// Gets the reason this plugin was loaded. - /// + /// public PluginLoadReason Reason { get; } - /// - /// Gets a value indicating whether auto-updates have already completed this session. - /// - public bool IsAutoUpdateComplete => Service.Get().IsAutoUpdateComplete; + /// + public bool IsAutoUpdateComplete => Service.GetNullable()?.IsAutoUpdateComplete ?? false; - /// - /// Gets the repository from which this plugin was installed. - /// - /// If a plugin was installed from the official/main repository, this will return the value of - /// . Developer plugins will return the value of - /// . - /// + /// public string SourceRepository { get; } - /// - /// Gets the current internal plugin name. - /// + /// public string InternalName => this.plugin.InternalName; - /// - /// Gets the plugin's manifest. - /// + /// public IPluginManifest Manifest => this.plugin.Manifest; - /// - /// Gets a value indicating whether this is a dev plugin. - /// + /// public bool IsDev => this.plugin.IsDev; - /// - /// Gets a value indicating whether this is a testing release of a plugin. - /// - /// - /// Dev plugins have undefined behavior for this value, but can be expected to return false. - /// + /// public bool IsTesting { get; } - /// - /// Gets the time that this plugin was loaded. - /// + /// public DateTime LoadTime { get; } - /// - /// Gets the UTC time that this plugin was loaded. - /// + /// public DateTime LoadTimeUTC { get; } - /// - /// Gets the timespan delta from when this plugin was loaded. - /// + /// public TimeSpan LoadTimeDelta => DateTime.Now - this.LoadTime; - /// - /// Gets the directory Dalamud assets are stored in. - /// + /// public DirectoryInfo DalamudAssetDirectory => Service.Get().AssetDirectory; - /// - /// Gets the location of your plugin assembly. - /// + /// public FileInfo AssemblyLocation => this.plugin.DllFile; - /// - /// Gets the directory your plugin configurations are stored in. - /// + /// public DirectoryInfo ConfigDirectory => new(this.GetPluginConfigDirectory()); - /// - /// Gets the config file of your plugin. - /// + /// public FileInfo ConfigFile => this.configs.GetConfigFile(this.plugin.InternalName); - /// - /// Gets the instance which allows you to draw UI into the game via ImGui draw calls. - /// + /// public IUiBuilder UiBuilder { get; private set; } - /// - /// Gets a value indicating whether Dalamud is running in Debug mode or the /xldev menu is open. This can occur on release builds. - /// + /// public bool IsDevMenuOpen => Service.GetNullable() is { IsDevMenuOpen: true }; // Can be null during boot - /// - /// Gets a value indicating whether a debugger is attached. - /// + /// public bool IsDebugging => Debugger.IsAttached; - /// - /// Gets the current UI language in two-letter iso format. - /// + /// public string UiLanguage { get; private set; } - /// - /// Gets serializer class with functions to remove special characters from strings. - /// + /// public ISanitizer Sanitizer { get; } - /// - /// Gets the chat type used by default for plugin messages. - /// + /// public XivChatType GeneralChatType { get; private set; } - /// - /// Gets a list of installed plugins along with their current state. - /// + /// public IEnumerable InstalledPlugins => Service.Get().InstalledPlugins.Select(p => new ExposedPlugin(p)); @@ -211,12 +161,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa /// internal UiBuilder LocalUiBuilder => this.uiBuilder; - /// - /// Opens the , with an optional search term. - /// - /// The page to open the installer to. Defaults to the "All Plugins" page. - /// An optional search text to input in the search box. - /// Returns false if the DalamudInterface was null. + /// public bool OpenPluginInstallerTo(PluginInstallerOpenKind openTo = PluginInstallerOpenKind.AllPlugins, string? searchText = null) { var dalamudInterface = Service.GetNullable(); // Can be null during boot @@ -231,12 +176,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa return true; } - /// - /// Opens the , with an optional search term. - /// - /// The tab to open the settings to. Defaults to the "General" tab. - /// An optional search text to input in the search box. - /// Returns false if the DalamudInterface was null. + /// public bool OpenDalamudSettingsTo(SettingsOpenKind openTo = SettingsOpenKind.General, string? searchText = null) { var dalamudInterface = Service.GetNullable(); // Can be null during boot @@ -251,10 +191,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa return true; } - /// - /// Opens the dev menu bar. - /// - /// Returns false if the DalamudInterface was null. + /// public bool OpenDeveloperMenu() { var dalamudInterface = Service.GetNullable(); // Can be null during boot @@ -267,104 +204,117 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa return true; } + /// + /// Gets the plugin the given assembly is part of. + /// + /// The assembly to check. + /// The plugin the given assembly is part of, or null if this is a shared assembly or if this information cannot be determined. + public IExposedPlugin? GetPlugin(Assembly assembly) + => AssemblyLoadContext.GetLoadContext(assembly) switch + { + null => null, + var context => this.GetPlugin(context), + }; + + /// + /// Gets the plugin that loads in the given context. + /// + /// The context to check. + /// 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. + public IExposedPlugin? GetPlugin(AssemblyLoadContext context) + => Service.Get().InstalledPlugins.FirstOrDefault(p => p.LoadsIn(context)) switch + { + null => null, + var p => new ExposedPlugin(p), + }; + #region IPC - /// + /// public T GetOrCreateData(string tag, Func dataGenerator) where T : class => Service.Get().GetOrCreateData(tag, dataGenerator); - /// + /// public void RelinquishData(string tag) => Service.Get().RelinquishData(tag); - /// + /// public bool TryGetData(string tag, [NotNullWhen(true)] out T? data) where T : class => Service.Get().TryGetData(tag, out data); - /// + /// public T? GetData(string tag) where T : class => Service.Get().GetData(tag); - /// - /// Gets an IPC provider. - /// - /// The return type for funcs. Use object if this is unused. - /// The name of the IPC registration. - /// An IPC provider. - /// This is thrown when the requested types do not match the previously registered types are different. + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// + /// public ICallGateProvider GetIpcProvider(string name) => new CallGatePubSub(name); - /// - /// Gets an IPC subscriber. - /// - /// The return type for funcs. Use object if this is unused. - /// The name of the IPC registration. - /// An IPC subscriber. + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); - /// + /// public ICallGateSubscriber GetIpcSubscriber(string name) => new CallGatePubSub(name); @@ -372,10 +322,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa #region Configuration - /// - /// Save a plugin configuration(inheriting IPluginConfiguration). - /// - /// The current configuration. + /// public void SavePluginConfig(IPluginConfiguration? currentConfig) { if (currentConfig == null) @@ -384,10 +331,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa this.configs.Save(currentConfig, this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId); } - /// - /// Get a previously saved plugin configuration or null if none was saved before. - /// - /// A previously saved config or null if none was saved before. + /// public IPluginConfiguration? GetPluginConfig() { // This is done to support json deserialization of plugin configurations @@ -411,22 +355,22 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa return this.configs.Load(this.plugin.InternalName, this.plugin.EffectiveWorkingPluginId); } - /// - /// Get the config directory. - /// - /// directory with path of AppData/XIVLauncher/pluginConfig/PluginInternalName. + /// public string GetPluginConfigDirectory() => this.configs.GetDirectory(this.plugin.InternalName); - /// - /// Get the loc directory. - /// - /// directory with path of AppData/XIVLauncher/pluginConfig/PluginInternalName/loc. + /// public string GetPluginLocDirectory() => this.configs.GetDirectory(Path.Combine(this.plugin.InternalName, "loc")); #endregion #region Dependency Injection + /// + public object? GetService(Type serviceType) + { + return this.plugin.ServiceScope.GetService(serviceType); + } + /// public T? Create(params object[] scopedObjects) where T : class { @@ -475,8 +419,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa #endregion - /// Unregister the plugin and dispose all references. - /// Dalamud internal use only. + /// public void Dispose() { Service.Get().RemoveChatLinkHandler(this.plugin.InternalName); @@ -489,7 +432,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa /// Dispatch the active plugins changed event. /// /// The event arguments containing information about the change. - internal void NotifyActivePluginsChanged(ActivePluginsChangedEventArgs args) + internal void NotifyActivePluginsChanged(IActivePluginsChangedEventArgs args) { foreach (var action in Delegate.EnumerateInvocationList(this.ActivePluginsChanged)) { diff --git a/Dalamud/Plugin/IActivePluginsChangedEventArgs.cs b/Dalamud/Plugin/IActivePluginsChangedEventArgs.cs new file mode 100644 index 000000000..17c4347c7 --- /dev/null +++ b/Dalamud/Plugin/IActivePluginsChangedEventArgs.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace Dalamud.Plugin; + +/// +/// Contains data about changes to the list of active plugins. +/// +public interface IActivePluginsChangedEventArgs +{ + /// + /// Gets the invalidation kind that caused this event to be fired. + /// + PluginListInvalidationKind Kind { get; } + + /// + /// Gets the InternalNames of affected plugins. + /// + IEnumerable AffectedInternalNames { get; } +} diff --git a/Dalamud/Plugin/IDalamudPluginInterface.cs b/Dalamud/Plugin/IDalamudPluginInterface.cs index 1a1a47403..d1b6977d4 100644 --- a/Dalamud/Plugin/IDalamudPluginInterface.cs +++ b/Dalamud/Plugin/IDalamudPluginInterface.cs @@ -1,13 +1,13 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Reflection; +using System.Runtime.Loader; using System.Threading.Tasks; using Dalamud.Configuration; using Dalamud.Game.Text; using Dalamud.Game.Text.Sanitizer; -using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Interface; using Dalamud.Interface.Internal.Windows.PluginInstaller; using Dalamud.Interface.Internal.Windows.Settings; @@ -15,13 +15,14 @@ using Dalamud.Plugin.Internal.Types.Manifest; using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Ipc.Exceptions; using Dalamud.Plugin.Ipc.Internal; +using Dalamud.Plugin.Services; namespace Dalamud.Plugin; /// /// This interface acts as an interface to various objects needed to interact with Dalamud and the game. /// -public interface IDalamudPluginInterface +public interface IDalamudPluginInterface : IServiceProvider { /// /// Delegate for localization change with two-letter iso lang code. @@ -33,7 +34,7 @@ public interface IDalamudPluginInterface /// Delegate for events that listen to changes to the list of active plugins. /// /// The event arguments containing information about the change. - public delegate void ActivePluginsChangedDelegate(ActivePluginsChangedEventArgs args); + public delegate void ActivePluginsChangedDelegate(IActivePluginsChangedEventArgs args); /// /// Event that gets fired when loc is changed @@ -179,6 +180,20 @@ public interface IDalamudPluginInterface /// Returns false if the DalamudInterface was null. bool OpenDeveloperMenu(); + /// + /// Gets the plugin the given assembly is part of. + /// + /// The assembly to check. + /// The plugin the given assembly is part of, or null if this is a shared assembly or if this information cannot be determined. + IExposedPlugin? GetPlugin(Assembly assembly); + + /// + /// Gets the plugin that loads in the given context. + /// + /// The context to check. + /// 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. + IExposedPlugin? GetPlugin(AssemblyLoadContext context); + /// T GetOrCreateData(string tag, Func dataGenerator) where T : class; diff --git a/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs b/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs index 169864bdf..3fc011a68 100644 --- a/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs +++ b/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs @@ -491,13 +491,10 @@ internal class AutoUpdateManager : IServiceType private bool CanUpdateOrNag() { - var condition = Service.Get(); + var clientState = Service.Get(); return this.IsPluginManagerReady() && !this.dalamudInterface.IsPluginInstallerOpen && - condition.OnlyAny(ConditionFlag.NormalConditions, - ConditionFlag.Jumping, - ConditionFlag.Mounted, - ConditionFlag.UsingFashionAccessory); + clientState.IsClientIdle(); } private bool IsPluginManagerReady() diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs index a4aa3919b..e2eded57c 100644 --- a/Dalamud/Plugin/Internal/PluginManager.cs +++ b/Dalamud/Plugin/Internal/PluginManager.cs @@ -278,26 +278,6 @@ internal class PluginManager : IInternalDisposableService return !manifest.IsHide; } - /// - /// Check if a manifest even has an available testing version. - /// - /// The manifest to test. - /// Whether a testing version is available. - public static bool HasTestingVersion(IPluginManifest manifest) - { - var av = manifest.AssemblyVersion; - var tv = manifest.TestingAssemblyVersion; - var hasTv = tv != null; - - if (hasTv) - { - return tv > av && - manifest.TestingDalamudApiLevel == DalamudApiLevel; - } - - return false; - } - /// /// Get a disposable that will lock plugin lists while it is not disposed. /// You must NEVER use this in async code. @@ -371,6 +351,23 @@ internal class PluginManager : IInternalDisposableService return this.configuration.PluginTestingOptIns!.Any(x => x.InternalName == manifest.InternalName); } + /// + /// For a given manifest, determine if the testing version can be used over the normal version. + /// The higher of the two versions is calculated after checking other settings. + /// + /// Manifest to check. + /// A value indicating whether testing can be used. + public bool CanUseTesting(IPluginManifest manifest) + { + if (!this.configuration.DoPluginTest) + return false; + + if (!manifest.TestingDalamudApiLevel.HasValue) + return false; + + return manifest.IsTestingExclusive || manifest.IsAvailableForTesting; + } + /// /// For a given manifest, determine if the testing version should be used over the normal version. /// The higher of the two versions is calculated after checking other settings. @@ -379,16 +376,7 @@ internal class PluginManager : IInternalDisposableService /// A value indicating whether testing should be used. public bool UseTesting(IPluginManifest manifest) { - if (!this.configuration.DoPluginTest) - return false; - - if (!this.HasTestingOptIn(manifest)) - return false; - - if (manifest.IsTestingExclusive) - return true; - - return HasTestingVersion(manifest); + return this.CanUseTesting(manifest) && this.HasTestingOptIn(manifest); } /// @@ -1208,9 +1196,18 @@ internal class PluginManager : IInternalDisposableService return false; // API level - we keep the API before this in the installer to show as "outdated" - var effectiveApiLevel = this.UseTesting(manifest) && manifest.TestingDalamudApiLevel != null ? manifest.TestingDalamudApiLevel.Value : manifest.DalamudApiLevel; - if (effectiveApiLevel < DalamudApiLevel - 1 && !this.LoadAllApiLevels) - return false; + if (!this.LoadAllApiLevels) + { + var effectiveDalamudApiLevel = + this.CanUseTesting(manifest) && + manifest.TestingDalamudApiLevel.HasValue && + manifest.TestingDalamudApiLevel.Value > manifest.DalamudApiLevel + ? manifest.TestingDalamudApiLevel.Value + : manifest.DalamudApiLevel; + + if (effectiveDalamudApiLevel < PluginManager.DalamudApiLevel - 1) + return false; + } // Banned if (this.IsManifestBanned(manifest)) @@ -1240,7 +1237,7 @@ internal class PluginManager : IInternalDisposableService } return this.bannedPlugins.Any(ban => (ban.Name == manifest.InternalName || ban.Name == Hash.GetStringSha256Hash(manifest.InternalName)) - && ban.AssemblyVersion >= versionToCheck); + && (ban.AssemblyVersion == null || ban.AssemblyVersion >= versionToCheck)); } /// @@ -1299,13 +1296,16 @@ internal class PluginManager : IInternalDisposableService /// The affected plugins. public void NotifyPluginsForStateChange(PluginListInvalidationKind kind, IEnumerable affectedInternalNames) { - foreach (var installedPlugin in this.installedPluginsList) + lock (this.pluginListLock) { - if (!installedPlugin.IsLoaded || installedPlugin.DalamudInterface == null) - continue; + foreach (var installedPlugin in this.installedPluginsList) + { + if (!installedPlugin.IsLoaded || installedPlugin.DalamudInterface == null) + continue; - installedPlugin.DalamudInterface.NotifyActivePluginsChanged( - new ActivePluginsChangedEventArgs(kind, affectedInternalNames)); + installedPlugin.DalamudInterface.NotifyActivePluginsChanged( + new ActivePluginsChangedEventArgs(kind, affectedInternalNames)); + } } } diff --git a/Dalamud/Plugin/Internal/Types/BannedPlugin.cs b/Dalamud/Plugin/Internal/Types/BannedPlugin.cs index a21bbf02b..384318a56 100644 --- a/Dalamud/Plugin/Internal/Types/BannedPlugin.cs +++ b/Dalamud/Plugin/Internal/Types/BannedPlugin.cs @@ -17,7 +17,7 @@ internal struct BannedPlugin /// Gets plugin assembly version. /// [JsonProperty] - public Version AssemblyVersion { get; private set; } + public Version? AssemblyVersion { get; private set; } /// /// Gets reason for the ban. diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs index 70b1db872..0197683ef 100644 --- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs +++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Runtime.ExceptionServices; +using System.Runtime.Loader; using System.Threading; using System.Threading.Tasks; @@ -301,7 +302,7 @@ internal class LocalPlugin : IAsyncDisposable throw new PluginPreconditionFailedException($"Unable to load {this.Name}, game is newer than applicable version {this.manifest.ApplicableVersion}"); // We want to allow loading dev plugins with a lower API level than the current Dalamud API level, for ease of development - if (this.manifest.EffectiveApiLevel < PluginManager.DalamudApiLevel && !pluginManager.LoadAllApiLevels && !this.IsDev) + if (!pluginManager.LoadAllApiLevels && !this.IsDev && this.manifest.EffectiveApiLevel < PluginManager.DalamudApiLevel) throw new PluginPreconditionFailedException($"Unable to load {this.Name}, incompatible API level {this.manifest.EffectiveApiLevel}"); // We might want to throw here? @@ -553,6 +554,14 @@ internal class LocalPlugin : IAsyncDisposable }); } + /// + /// Checks whether this plugin loads in the given load context. + /// + /// The load context to check. + /// Whether this plugin loads in the given load context. + public bool LoadsIn(AssemblyLoadContext context) + => this.loader?.LoadContext == context; + /// /// Save this plugin manifest. /// diff --git a/Dalamud/Plugin/Internal/Types/Manifest/IPluginManifest.cs b/Dalamud/Plugin/Internal/Types/Manifest/IPluginManifest.cs index 5ab5abbc1..c770c80be 100644 --- a/Dalamud/Plugin/Internal/Types/Manifest/IPluginManifest.cs +++ b/Dalamud/Plugin/Internal/Types/Manifest/IPluginManifest.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Dalamud.Plugin.Internal.Types.Manifest; @@ -118,4 +118,9 @@ public interface IPluginManifest /// Gets an URL for the plugin's icon. /// public string? IconUrl { get; } + + /// + /// Gets a value indicating whether this plugin is eligible for testing. + /// + public bool IsAvailableForTesting { get; } } diff --git a/Dalamud/Plugin/Internal/Types/Manifest/RemotePluginManifest.cs b/Dalamud/Plugin/Internal/Types/Manifest/RemotePluginManifest.cs index e3d99a85a..47e92cd84 100644 --- a/Dalamud/Plugin/Internal/Types/Manifest/RemotePluginManifest.cs +++ b/Dalamud/Plugin/Internal/Types/Manifest/RemotePluginManifest.cs @@ -21,9 +21,4 @@ internal record RemotePluginManifest : PluginManifest /// Gets or sets the changelog to be shown when obtaining the testing version of the plugin. /// public string? TestingChangelog { get; set; } - - /// - /// Gets a value indicating whether this plugin is eligible for testing. - /// - public bool IsAvailableForTesting => this.TestingAssemblyVersion != null && this.TestingAssemblyVersion > this.AssemblyVersion; } diff --git a/Dalamud/Plugin/Internal/Types/PluginManifest.cs b/Dalamud/Plugin/Internal/Types/PluginManifest.cs index 57001d63b..cbf69bb5e 100644 --- a/Dalamud/Plugin/Internal/Types/PluginManifest.cs +++ b/Dalamud/Plugin/Internal/Types/PluginManifest.cs @@ -160,4 +160,11 @@ internal record PluginManifest : IPluginManifest /// [JsonProperty("_Dip17Channel")] public string? Dip17Channel { get; init; } + + /// + [JsonIgnore] + public bool IsAvailableForTesting + => this.TestingAssemblyVersion != null && + this.TestingAssemblyVersion > this.AssemblyVersion && + this.TestingDalamudApiLevel == PluginManager.DalamudApiLevel; } diff --git a/Dalamud/Plugin/SelfTest/ISelfTestRegistry.cs b/Dalamud/Plugin/SelfTest/ISelfTestRegistry.cs new file mode 100644 index 000000000..af3b583c9 --- /dev/null +++ b/Dalamud/Plugin/SelfTest/ISelfTestRegistry.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; + +namespace Dalamud.Plugin.SelfTest; + +/// +/// Interface for registering and unregistering self-test steps from plugins. +/// +/// +/// Registering custom self-test steps for your plugin: +/// +/// [PluginService] +/// public ISelfTestRegistry SelfTestRegistry { get; init; } +/// +/// // In your plugin initialization +/// this.SelfTestRegistry.RegisterTestSteps([ +/// new MyCustomSelfTestStep(), +/// new AnotherSelfTestStep() +/// ]); +/// +/// +/// Creating a custom self-test step: +/// +/// public class MyCustomSelfTestStep : ISelfTestStep +/// { +/// public string Name => "My Custom Test"; +/// +/// public SelfTestStepResult RunStep() +/// { +/// // Your test logic here +/// if (/* test condition passes */) +/// return SelfTestStepResult.Pass; +/// +/// if (/* test condition fails */) +/// return SelfTestStepResult.Fail; +/// +/// // Still waiting for test to complete +/// return SelfTestStepResult.Waiting; +/// } +/// +/// public void CleanUp() +/// { +/// // Clean up any resources used by the test +/// } +/// } +/// +/// +public interface ISelfTestRegistry +{ + /// + /// Registers the self-test steps for this plugin. + /// + /// The test steps to register. + public void RegisterTestSteps(IEnumerable steps); +} diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ISelfTestStep.cs b/Dalamud/Plugin/SelfTest/ISelfTestStep.cs similarity index 85% rename from Dalamud/Interface/Internal/Windows/SelfTest/Steps/ISelfTestStep.cs rename to Dalamud/Plugin/SelfTest/ISelfTestStep.cs index 529d788ab..2b0de7bf3 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/Steps/ISelfTestStep.cs +++ b/Dalamud/Plugin/SelfTest/ISelfTestStep.cs @@ -1,9 +1,9 @@ -namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps; +namespace Dalamud.Plugin.SelfTest; /// /// Interface for test implementations. /// -internal interface ISelfTestStep +public interface ISelfTestStep { /// /// Gets the name of the test. diff --git a/Dalamud/Plugin/SelfTest/Internal/SelfTestGroup.cs b/Dalamud/Plugin/SelfTest/Internal/SelfTestGroup.cs new file mode 100644 index 000000000..e2bc80f4b --- /dev/null +++ b/Dalamud/Plugin/SelfTest/Internal/SelfTestGroup.cs @@ -0,0 +1,28 @@ +namespace Dalamud.Plugin.SelfTest.Internal; + +/// +/// Represents a self-test group with its loaded/unloaded state. +/// +internal class SelfTestGroup +{ + /// + /// Initializes a new instance of the class. + /// + /// The name of the test group. + /// Whether the group is currently loaded. + public SelfTestGroup(string name, bool loaded = true) + { + this.Name = name; + this.Loaded = loaded; + } + + /// + /// Gets the name of the test group. + /// + public string Name { get; } + + /// + /// Gets or sets a value indicating whether this test group is currently loaded. + /// + public bool Loaded { get; set; } +} diff --git a/Dalamud/Plugin/SelfTest/Internal/SelfTestRegistry.cs b/Dalamud/Plugin/SelfTest/Internal/SelfTestRegistry.cs new file mode 100644 index 000000000..aa410b2c4 --- /dev/null +++ b/Dalamud/Plugin/SelfTest/Internal/SelfTestRegistry.cs @@ -0,0 +1,124 @@ +using System.Collections.Generic; +using System.Linq; + +using Dalamud.Logging.Internal; +using Dalamud.Plugin.Internal.Types; + +namespace Dalamud.Plugin.SelfTest.Internal; + +/// +/// Registry for self-tests that can be run in the SelfTest window. +/// +[ServiceManager.EarlyLoadedService] +internal class SelfTestRegistry : IServiceType +{ + /// + /// The name of the Dalamud test group. + /// + public const string DalamudTestGroup = "Dalamud"; + + private static readonly ModuleLog Log = new("SelfTestRegistry"); + + private List dalamudSelfTests = new(); + private List pluginSelfTests = new(); + private Dictionary allGroups = new(); + + /// + /// Initializes a new instance of the class. + /// + [ServiceManager.ServiceConstructor] + public SelfTestRegistry() + { + } + + /// + /// Gets all available self test groups. + /// + public IEnumerable SelfTestGroups + { + get + { + // Always return Dalamud group first, then plugin groups + if (this.allGroups.TryGetValue(DalamudTestGroup, out var dalamudGroup)) + { + yield return dalamudGroup; + } + + foreach (var group in this.allGroups.Values) + { + if (group.Name != DalamudTestGroup) + { + yield return group; + } + } + } + } + + /// + /// Gets all self tests from all groups. + /// + public IEnumerable SelfTests => this.dalamudSelfTests.Concat(this.pluginSelfTests); + + /// + /// Registers Dalamud self test steps. + /// + /// The steps to register. + public void RegisterDalamudSelfTestSteps(IEnumerable steps) + { + // Ensure Dalamud group exists and is loaded + if (!this.allGroups.ContainsKey(DalamudTestGroup)) + { + this.allGroups[DalamudTestGroup] = new SelfTestGroup(DalamudTestGroup, loaded: true); + } + else + { + this.allGroups[DalamudTestGroup].Loaded = true; + } + + this.dalamudSelfTests.AddRange(steps.Select(step => SelfTestWithResults.FromDalamudStep(step))); + } + + /// + /// Registers plugin self test steps. + /// + /// The plugin registering the tests. + /// The steps to register. + public void RegisterPluginSelfTestSteps(LocalPlugin plugin, IEnumerable steps) + { + // Ensure plugin group exists and is loaded + if (!this.allGroups.ContainsKey(plugin.InternalName)) + { + this.allGroups[plugin.InternalName] = new SelfTestGroup(plugin.InternalName, loaded: true); + } + else + { + this.allGroups[plugin.InternalName].Loaded = true; + } + + this.pluginSelfTests.AddRange(steps.Select(step => SelfTestWithResults.FromPluginStep(plugin, step))); + } + + /// + /// Unregisters all self test steps for a plugin. + /// + /// The plugin to unregister tests for. + public void UnregisterPluginSelfTestSteps(LocalPlugin plugin) + { + // Clean up existing tests for this plugin + this.pluginSelfTests.ForEach(test => + { + if (test.Plugin == plugin) + { + test.Unload(); + } + }); + + this.pluginSelfTests.RemoveAll(test => test.Plugin == plugin); + + // Mark group as unloaded if it exists + if (this.allGroups.ContainsKey(plugin.InternalName)) + { + this.allGroups[plugin.InternalName].Loaded = false; + } + } +} diff --git a/Dalamud/Plugin/SelfTest/Internal/SelfTestRegistryPluginScoped.cs b/Dalamud/Plugin/SelfTest/Internal/SelfTestRegistryPluginScoped.cs new file mode 100644 index 000000000..e3835ddbc --- /dev/null +++ b/Dalamud/Plugin/SelfTest/Internal/SelfTestRegistryPluginScoped.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; + +using Dalamud.IoC; +using Dalamud.IoC.Internal; +using Dalamud.Plugin.Internal.Types; +using Dalamud.Plugin.Services; + +namespace Dalamud.Plugin.SelfTest.Internal; + +/// +/// Plugin-scoped version of SelfTestRegistry. +/// +[PluginInterface] +[ServiceManager.ScopedService] +[ResolveVia] +internal class SelfTestRegistryPluginScoped : ISelfTestRegistry, IInternalDisposableService, IDalamudService +{ + [ServiceManager.ServiceDependency] + private readonly SelfTestRegistry selfTestRegistry = Service.Get(); + + private readonly LocalPlugin plugin; + + /// + /// Initializes a new instance of the class. + /// + /// The plugin this service belongs to. + [ServiceManager.ServiceConstructor] + public SelfTestRegistryPluginScoped(LocalPlugin plugin) + { + this.plugin = plugin; + } + + /// + /// Gets the plugin name. + /// + public string PluginName { get; private set; } + + /// + /// Registers test steps for this plugin. + /// + /// The test steps to register. + public void RegisterTestSteps(IEnumerable steps) + { + this.selfTestRegistry.RegisterPluginSelfTestSteps(this.plugin, steps); + } + + /// + void IInternalDisposableService.DisposeService() + { + this.selfTestRegistry.UnregisterPluginSelfTestSteps(this.plugin); + } +} diff --git a/Dalamud/Plugin/SelfTest/Internal/SelfTestWithResults.cs b/Dalamud/Plugin/SelfTest/Internal/SelfTestWithResults.cs new file mode 100644 index 000000000..3c03f66a8 --- /dev/null +++ b/Dalamud/Plugin/SelfTest/Internal/SelfTestWithResults.cs @@ -0,0 +1,173 @@ +using Dalamud.Logging.Internal; +using Dalamud.Plugin.Internal.Types; + +namespace Dalamud.Plugin.SelfTest.Internal; + +/// +/// A self test step with result tracking. +/// +internal class SelfTestWithResults +{ + private static readonly ModuleLog Log = new("SelfTest"); + + /// + /// Initializes a new instance of the class. + /// + /// The plugin providing this test. + /// The test group name. + /// The test step. + public SelfTestWithResults(LocalPlugin plugin, string group, ISelfTestStep step) + { + this.Plugin = plugin; + this.Group = group; + this.Step = step; + } + + /// + /// Gets the test group name. + /// + public string Group { get; private set; } + + /// + /// Gets the plugin that defined these tests. null for Dalamud tests. + /// + public LocalPlugin? Plugin { get; private set; } + + /// + /// Gets the test name. + /// + public string Name { get => this.Step.Name; } + + /// + /// Gets a value indicating whether the test has run and finished. + /// + public bool Finished => this.Result == SelfTestStepResult.Fail || this.Result == SelfTestStepResult.Pass; + + /// + /// Gets a value indicating whether the plugin that provided this test has been unloaded. + /// + public bool Unloaded => this.Step == null; + + /// + /// Gets the most recent result of running this test. + /// + public SelfTestStepResult Result { get; private set; } = SelfTestStepResult.NotRan; + + /// + /// Gets the last time this test was started. + /// + public DateTimeOffset? StartTime { get; private set; } = null; + + /// + /// Gets how long it took (or is taking) for this test to execute. + /// + public TimeSpan? Duration { get; private set; } = null; + + /// + /// Gets or sets the Step that our results are for. + /// + /// If null it means the Plugin that provided this test has been unloaded and we can't use this test anymore. + /// + private ISelfTestStep? Step { get; set; } + + /// + /// Creates a SelfTestWithResults from a Dalamud step. + /// + /// The step to wrap. + /// A new SelfTestWithResults instance. + public static SelfTestWithResults FromDalamudStep(ISelfTestStep step) + { + return new SelfTestWithResults(plugin: null, group: "Dalamud", step: step); + } + + /// + /// Creates a SelfTestWithResults from a plugin step. + /// + /// The plugin providing the step. + /// The step to wrap. + /// A new SelfTestWithResults instance. + public static SelfTestWithResults FromPluginStep(LocalPlugin plugin, ISelfTestStep step) + { + return new SelfTestWithResults(plugin: plugin, group: plugin.InternalName, step: step); + } + + /// + /// Reset the test. + /// + public void Reset() + { + this.Result = SelfTestStepResult.NotRan; + this.StartTime = null; + this.Duration = null; + } + + /// + /// Finish the currently running test and clean up any state. This preserves test run results. + /// + public void Finish() + { + if (this.Step == null) + { + return; + } + + if (this.Result == SelfTestStepResult.NotRan) + { + return; + } + + this.Step.CleanUp(); + } + + /// + /// Steps the state of this Self Test. This should be called every frame that we care about the results of this test. + /// + public void DrawAndStep() + { + // If we've been unloaded then there's nothing to do. + if (this.Step == null) + { + return; + } + + // If we have already finished then there's nothing to do + if (this.Finished) + { + return; + } + + // Otherwise, we assume that calling this functions means we are running the test. + if (this.Result == SelfTestStepResult.NotRan) + { + this.StartTime = DateTimeOffset.Now; + this.Result = SelfTestStepResult.Waiting; + } + + try + { + this.Result = this.Step.RunStep(); + } + catch (Exception ex) + { + Log.Error(ex, $"Step failed: {this.Name} ({this.Group})"); + this.Result = SelfTestStepResult.Fail; + } + + this.Duration = DateTimeOffset.Now - this.StartTime; + + // If we ran and finished we need to clean up + if (this.Finished) + { + this.Finish(); + } + } + + /// + /// Unloads the test and cleans up. + /// + public void Unload() + { + this.Finish(); + this.Step = null; + } +} diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestStepResult.cs b/Dalamud/Plugin/SelfTest/SelfTestStepResult.cs similarity index 81% rename from Dalamud/Interface/Internal/Windows/SelfTest/SelfTestStepResult.cs rename to Dalamud/Plugin/SelfTest/SelfTestStepResult.cs index 5abc8e2ed..0eaf77381 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestStepResult.cs +++ b/Dalamud/Plugin/SelfTest/SelfTestStepResult.cs @@ -1,9 +1,9 @@ -namespace Dalamud.Interface.Internal.Windows.SelfTest; +namespace Dalamud.Plugin.SelfTest; /// /// Enum declaring result states of tests. /// -internal enum SelfTestStepResult +public enum SelfTestStepResult { /// /// Test was not ran. diff --git a/Dalamud/Plugin/Services/IAddonEventManager.cs b/Dalamud/Plugin/Services/IAddonEventManager.cs index c6499e4e2..6b7b1166e 100644 --- a/Dalamud/Plugin/Services/IAddonEventManager.cs +++ b/Dalamud/Plugin/Services/IAddonEventManager.cs @@ -1,4 +1,4 @@ -using Dalamud.Game.Addon.Events; +using Dalamud.Game.Addon.Events; using Dalamud.Game.Addon.Events.EventDataTypes; namespace Dalamud.Plugin.Services; @@ -6,7 +6,7 @@ namespace Dalamud.Plugin.Services; /// /// Service provider for addon event management. /// -public interface IAddonEventManager +public interface IAddonEventManager : IDalamudService { /// /// Delegate to be called when an event is received. diff --git a/Dalamud/Plugin/Services/IAddonLifecycle.cs b/Dalamud/Plugin/Services/IAddonLifecycle.cs index ebf629b85..1269b13dc 100644 --- a/Dalamud/Plugin/Services/IAddonLifecycle.cs +++ b/Dalamud/Plugin/Services/IAddonLifecycle.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Runtime.InteropServices; using Dalamud.Game.Addon.Lifecycle; @@ -9,7 +9,7 @@ namespace Dalamud.Plugin.Services; /// /// This class provides events for in-game addon lifecycles. /// -public interface IAddonLifecycle +public interface IAddonLifecycle : IDalamudService { /// /// Delegate for receiving addon lifecycle event messages. diff --git a/Dalamud/Plugin/Services/IAetheryteList.cs b/Dalamud/Plugin/Services/IAetheryteList.cs index 88c2ff616..58b82ebf6 100644 --- a/Dalamud/Plugin/Services/IAetheryteList.cs +++ b/Dalamud/Plugin/Services/IAetheryteList.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.ClientState.Aetherytes; @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// This collection represents the list of available Aetherytes in the Teleport window. /// -public interface IAetheryteList : IReadOnlyCollection +public interface IAetheryteList : IDalamudService, IReadOnlyCollection { /// /// Gets the amount of Aetherytes the local player has unlocked. diff --git a/Dalamud/Plugin/Services/IBuddyList.cs b/Dalamud/Plugin/Services/IBuddyList.cs index 77c0b9c17..8d3790b6d 100644 --- a/Dalamud/Plugin/Services/IBuddyList.cs +++ b/Dalamud/Plugin/Services/IBuddyList.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.ClientState.Buddy; @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// This collection represents the buddies present in your squadron or trust party. /// It does not include the local player. /// -public interface IBuddyList : IReadOnlyCollection +public interface IBuddyList : IDalamudService, IReadOnlyCollection { /// /// Gets the amount of battle buddies the local player has. diff --git a/Dalamud/Plugin/Services/IChatGui.cs b/Dalamud/Plugin/Services/IChatGui.cs index ab595dc3f..572ac6c95 100644 --- a/Dalamud/Plugin/Services/IChatGui.cs +++ b/Dalamud/Plugin/Services/IChatGui.cs @@ -10,7 +10,7 @@ namespace Dalamud.Plugin.Services; /// /// This class handles interacting with the native chat UI. /// -public interface IChatGui +public interface IChatGui : IDalamudService { /// /// A delegate type used with the event. @@ -83,20 +83,21 @@ public interface IChatGui /// /// Gets the dictionary of Dalamud Link Handlers. /// - public IReadOnlyDictionary<(string PluginName, Guid CommandId), Action> RegisteredLinkHandlers { get; } + public IReadOnlyDictionary<(string PluginName, uint CommandId), Action> RegisteredLinkHandlers { get; } /// /// Register a chat link handler. /// + /// The ID of the command. /// The action to be executed. /// Returns an SeString payload for the link. - public DalamudLinkPayload AddChatLinkHandler(Action commandAction); + public DalamudLinkPayload AddChatLinkHandler(uint commandId, Action commandAction); /// /// Remove a chat link handler. /// /// The ID of the command. - public void RemoveChatLinkHandler(Guid commandId); + public void RemoveChatLinkHandler(uint commandId); /// /// Removes all chat link handlers registered by the plugin. diff --git a/Dalamud/Plugin/Services/IClientState.cs b/Dalamud/Plugin/Services/IClientState.cs index 60d8a17e2..2555b3b30 100644 --- a/Dalamud/Plugin/Services/IClientState.cs +++ b/Dalamud/Plugin/Services/IClientState.cs @@ -1,4 +1,5 @@ using Dalamud.Game; +using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Objects.SubKinds; @@ -7,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// /// This class represents the state of the game client at the time of access. /// -public interface IClientState +public interface IClientState : IDalamudService { /// /// A delegate type used for the event. @@ -29,11 +30,26 @@ public interface IClientState /// The success/failure code. public delegate void LogoutDelegate(int type, int code); + /// + /// Event that gets fired when the game initializes a zone. + /// + public event Action ZoneInit; + /// /// Event that gets fired when the current Territory changes. /// public event Action TerritoryChanged; + /// + /// Event that gets fired when the current Map changes. + /// + public event Action MapIdChanged; + + /// + /// Event that gets fired when the current zone Instance changes. + /// + public event Action InstanceChanged; + /// /// Event that fires when a characters ClassJob changed. /// @@ -85,14 +101,21 @@ public interface IClientState /// public uint MapId { get; } + /// + /// Gets the instance number of the current zone, used when multiple copies of an area are active. + /// + public uint Instance { get; } + /// /// Gets the local player character, if one is present. /// + [Obsolete($"Use {nameof(IPlayerState)} or {nameof(IObjectTable)}.{nameof(IObjectTable.LocalPlayer)} if necessary.")] public IPlayerCharacter? LocalPlayer { get; } /// /// Gets the content ID of the local character. /// + [Obsolete($"Use {nameof(IPlayerState)}.{nameof(IPlayerState.ContentId)}")] public ulong LocalContentId { get; } /// diff --git a/Dalamud/Plugin/Services/ICommandManager.cs b/Dalamud/Plugin/Services/ICommandManager.cs index a6bc4763f..46138cd71 100644 --- a/Dalamud/Plugin/Services/ICommandManager.cs +++ b/Dalamud/Plugin/Services/ICommandManager.cs @@ -1,4 +1,4 @@ -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using Dalamud.Game.Command; @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// This class manages registered in-game slash commands. /// -public interface ICommandManager +public interface ICommandManager : IDalamudService { /// /// Gets a read-only list of all registered commands. diff --git a/Dalamud/Plugin/Services/ICondition.cs b/Dalamud/Plugin/Services/ICondition.cs index 4ea9e7f76..c37117f3c 100644 --- a/Dalamud/Plugin/Services/ICondition.cs +++ b/Dalamud/Plugin/Services/ICondition.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.ClientState.Conditions; @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// Provides access to conditions (generally player state). You can check whether a player is in combat, mounted, etc. /// -public interface ICondition +public interface ICondition : IDalamudService { /// /// A delegate type used with the event. diff --git a/Dalamud/Plugin/Services/IConsole.cs b/Dalamud/Plugin/Services/IConsole.cs index 0b6832efb..be920a5c9 100644 --- a/Dalamud/Plugin/Services/IConsole.cs +++ b/Dalamud/Plugin/Services/IConsole.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using Dalamud.Console; @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// Provides functions to register console commands and variables. /// [Experimental("Dalamud001")] -public interface IConsole +public interface IConsole : IDalamudService { /// /// Gets this plugin's namespace prefix, derived off its internal name. diff --git a/Dalamud/Plugin/Services/IContextMenu.cs b/Dalamud/Plugin/Services/IContextMenu.cs index 02f773441..ed99f595e 100644 --- a/Dalamud/Plugin/Services/IContextMenu.cs +++ b/Dalamud/Plugin/Services/IContextMenu.cs @@ -5,7 +5,7 @@ namespace Dalamud.Plugin.Services; /// /// This class provides methods for interacting with the game's context menu. /// -public interface IContextMenu +public interface IContextMenu : IDalamudService { /// /// A delegate type used for the event. diff --git a/Dalamud/Plugin/Services/IDalamudService.cs b/Dalamud/Plugin/Services/IDalamudService.cs new file mode 100644 index 000000000..1472b27da --- /dev/null +++ b/Dalamud/Plugin/Services/IDalamudService.cs @@ -0,0 +1,10 @@ +namespace Dalamud.Plugin.Services; + +/// +/// Marker interface for Dalamud services. +/// +/// +/// This interface is implemented by all services provided through Dalamud's +/// dependency injection system. +/// +public interface IDalamudService; diff --git a/Dalamud/Plugin/Services/IDataManager.cs b/Dalamud/Plugin/Services/IDataManager.cs index 65c51a9fb..474d34ecb 100644 --- a/Dalamud/Plugin/Services/IDataManager.cs +++ b/Dalamud/Plugin/Services/IDataManager.cs @@ -14,7 +14,7 @@ namespace Dalamud.Plugin.Services; /// /// This class provides data for Dalamud-internal features, but can also be used by plugins if needed. /// -public interface IDataManager +public interface IDataManager : IDalamudService { /// /// Gets the current game client language. diff --git a/Dalamud/Plugin/Services/IDtrBar.cs b/Dalamud/Plugin/Services/IDtrBar.cs index 8ab34c6f2..a24327e23 100644 --- a/Dalamud/Plugin/Services/IDtrBar.cs +++ b/Dalamud/Plugin/Services/IDtrBar.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.Gui.Dtr; using Dalamud.Game.Text.SeStringHandling; @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// /// Class used to interface with the server info bar. /// -public interface IDtrBar +public interface IDtrBar : IDalamudService { /// /// Gets a read-only copy of the list of all DTR bar entries. diff --git a/Dalamud/Plugin/Services/IDutyState.cs b/Dalamud/Plugin/Services/IDutyState.cs index 3d49f68cb..9ad2e3c24 100644 --- a/Dalamud/Plugin/Services/IDutyState.cs +++ b/Dalamud/Plugin/Services/IDutyState.cs @@ -1,9 +1,9 @@ -namespace Dalamud.Plugin.Services; +namespace Dalamud.Plugin.Services; /// /// This class represents the state of the currently occupied duty. /// -public interface IDutyState +public interface IDutyState : IDalamudService { /// /// Event that gets fired when the duty starts. diff --git a/Dalamud/Plugin/Services/IFateTable.cs b/Dalamud/Plugin/Services/IFateTable.cs index d10141050..3392d8e23 100644 --- a/Dalamud/Plugin/Services/IFateTable.cs +++ b/Dalamud/Plugin/Services/IFateTable.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.ClientState.Fates; @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// This collection represents the currently available Fate events. /// -public interface IFateTable : IReadOnlyCollection +public interface IFateTable : IDalamudService, IReadOnlyCollection { /// /// Gets the address of the Fate table. diff --git a/Dalamud/Plugin/Services/IFlyTextGui.cs b/Dalamud/Plugin/Services/IFlyTextGui.cs index 04fae351d..6c0e40fd6 100644 --- a/Dalamud/Plugin/Services/IFlyTextGui.cs +++ b/Dalamud/Plugin/Services/IFlyTextGui.cs @@ -1,4 +1,4 @@ -using Dalamud.Game.Gui.FlyText; +using Dalamud.Game.Gui.FlyText; using Dalamud.Game.Text.SeStringHandling; namespace Dalamud.Plugin.Services; @@ -6,7 +6,7 @@ namespace Dalamud.Plugin.Services; /// /// This class facilitates interacting with and creating native in-game "fly text". /// -public interface IFlyTextGui +public interface IFlyTextGui : IDalamudService { /// /// The delegate defining the type for the FlyText event. diff --git a/Dalamud/Plugin/Services/IFramework.cs b/Dalamud/Plugin/Services/IFramework.cs index f1a4b6906..3524ca668 100644 --- a/Dalamud/Plugin/Services/IFramework.cs +++ b/Dalamud/Plugin/Services/IFramework.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -24,7 +24,7 @@ namespace Dalamud.Plugin.Services; /// See to see the difference in behaviors, and how would a misuse of these /// functions result in a deadlock. /// -public interface IFramework +public interface IFramework : IDalamudService { /// /// A delegate type used with the event. diff --git a/Dalamud/Plugin/Services/IGameConfig.cs b/Dalamud/Plugin/Services/IGameConfig.cs index 5d8378659..10883c6d1 100644 --- a/Dalamud/Plugin/Services/IGameConfig.cs +++ b/Dalamud/Plugin/Services/IGameConfig.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Diagnostics; using Dalamud.Game.Config; using Dalamud.Plugin.Internal.Types; @@ -17,7 +17,7 @@ namespace Dalamud.Plugin.Services; /// If property access from the plugin constructor is desired, do the value retrieval asynchronously via /// ; do not wait for the result right away. /// -public interface IGameConfig +public interface IGameConfig : IDalamudService { /// /// Event which is fired when any game config option is changed. diff --git a/Dalamud/Plugin/Services/IGameGui.cs b/Dalamud/Plugin/Services/IGameGui.cs index 773ba61b4..933252ff4 100644 --- a/Dalamud/Plugin/Services/IGameGui.cs +++ b/Dalamud/Plugin/Services/IGameGui.cs @@ -9,7 +9,7 @@ namespace Dalamud.Plugin.Services; /// /// A class handling many aspects of the in-game UI. /// -public unsafe interface IGameGui +public unsafe interface IGameGui : IDalamudService { /// /// Event which is fired when the game UI hiding is toggled. @@ -26,6 +26,12 @@ public unsafe interface IGameGui /// public event EventHandler HoveredActionChanged; + /// + /// Fired when the game sets one or more values, + /// used by agents to conditionally update their addons. + /// + event Action AgentUpdate; + /// /// Gets a value indicating whether the game UI is hidden. /// @@ -36,7 +42,7 @@ public unsafe interface IGameGui /// If > 1.000.000, subtract 1.000.000 and treat it as HQ. /// public ulong HoveredItem { get; set; } - + /// /// Gets the action ID that is current hovered by the player. 0 when no action is hovered. /// @@ -89,6 +95,15 @@ public unsafe interface IGameGui /// A pointer wrapper to the addon. public AtkUnitBasePtr GetAddonByName(string name, int index = 1); + /// + /// Gets the pointer to the Addon with the given name and index. + /// + /// Name of addon to find. + /// Index of addon to find (1-indexed). + /// A pointer wrapper to the addon. + /// Type of addon pointer AtkUnitBase or any derived struct. + public T* GetAddonByName(string name, int index = 1) where T : unmanaged; + /// /// Find the agent associated with an addon, if possible. /// diff --git a/Dalamud/Plugin/Services/IGameInteropProvider.cs b/Dalamud/Plugin/Services/IGameInteropProvider.cs index 99e36c7ed..645d70ac6 100644 --- a/Dalamud/Plugin/Services/IGameInteropProvider.cs +++ b/Dalamud/Plugin/Services/IGameInteropProvider.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Diagnostics; using Dalamud.Hooking; using Dalamud.Utility.Signatures; @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// /// Service responsible for the creation of hooks. /// -public interface IGameInteropProvider +public interface IGameInteropProvider : IDalamudService { /// /// Available hooking backends. diff --git a/Dalamud/Plugin/Services/IGameInventory.cs b/Dalamud/Plugin/Services/IGameInventory.cs index 0dff1ff03..cba1c9872 100644 --- a/Dalamud/Plugin/Services/IGameInventory.cs +++ b/Dalamud/Plugin/Services/IGameInventory.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.Inventory; using Dalamud.Game.Inventory.InventoryEventArgTypes; @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// /// This class provides events for the in-game inventory. /// -public interface IGameInventory +public interface IGameInventory : IDalamudService { /// /// Delegate function to be called when inventories have been changed. diff --git a/Dalamud/Plugin/Services/IGameLifecycle.cs b/Dalamud/Plugin/Services/IGameLifecycle.cs index caa64ed23..8fae3fc0e 100644 --- a/Dalamud/Plugin/Services/IGameLifecycle.cs +++ b/Dalamud/Plugin/Services/IGameLifecycle.cs @@ -1,11 +1,11 @@ -using System.Threading; +using System.Threading; namespace Dalamud.Plugin.Services; /// /// Class offering cancellation tokens for common gameplay events. /// -public interface IGameLifecycle +public interface IGameLifecycle : IDalamudService { /// /// Gets a token that is cancelled when Dalamud is unloading. diff --git a/Dalamud/Plugin/Services/IGameNetwork.cs b/Dalamud/Plugin/Services/IGameNetwork.cs index 969176da7..4abf20834 100644 --- a/Dalamud/Plugin/Services/IGameNetwork.cs +++ b/Dalamud/Plugin/Services/IGameNetwork.cs @@ -1,4 +1,4 @@ -using Dalamud.Game.Network; +using Dalamud.Game.Network; namespace Dalamud.Plugin.Services; @@ -6,7 +6,7 @@ namespace Dalamud.Plugin.Services; /// This class handles interacting with game network events. /// [Obsolete("Will be removed in a future release. Use packet handler hooks instead.", true)] -public interface IGameNetwork +public interface IGameNetwork : IDalamudService { // TODO(v9): we shouldn't be passing pointers to the actual data here diff --git a/Dalamud/Plugin/Services/IGamepadState.cs b/Dalamud/Plugin/Services/IGamepadState.cs index 2816c927e..bdb07b91b 100644 --- a/Dalamud/Plugin/Services/IGamepadState.cs +++ b/Dalamud/Plugin/Services/IGamepadState.cs @@ -1,4 +1,4 @@ -using System.Numerics; +using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState.GamePad; @@ -10,7 +10,7 @@ namespace Dalamud.Plugin.Services; /// /// Will block game's gamepad input if is set. /// -public interface IGamepadState +public interface IGamepadState : IDalamudService { /// /// Gets the pointer to the current instance of the GamepadInput struct. diff --git a/Dalamud/Plugin/Services/IJobGauges.cs b/Dalamud/Plugin/Services/IJobGauges.cs index 4489a7be7..3313de7f6 100644 --- a/Dalamud/Plugin/Services/IJobGauges.cs +++ b/Dalamud/Plugin/Services/IJobGauges.cs @@ -1,11 +1,11 @@ -using Dalamud.Game.ClientState.JobGauge.Types; +using Dalamud.Game.ClientState.JobGauge.Types; namespace Dalamud.Plugin.Services; /// /// This class converts in-memory Job gauge data to structs. /// -public interface IJobGauges +public interface IJobGauges : IDalamudService { /// /// Gets the address of the JobGauge data. diff --git a/Dalamud/Plugin/Services/IKeyState.cs b/Dalamud/Plugin/Services/IKeyState.cs index de78978ca..06d6c9b49 100644 --- a/Dalamud/Plugin/Services/IKeyState.cs +++ b/Dalamud/Plugin/Services/IKeyState.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.ClientState.Keys; @@ -16,7 +16,7 @@ namespace Dalamud.Plugin.Services; /// index & 2 = key up (ephemeral). /// index & 3 = short key press (ephemeral). /// -public interface IKeyState +public interface IKeyState : IDalamudService { /// /// Get or set the key-pressed state for a given vkCode. diff --git a/Dalamud/Plugin/Services/IMarketBoard.cs b/Dalamud/Plugin/Services/IMarketBoard.cs index 3fded6987..0bdfad175 100644 --- a/Dalamud/Plugin/Services/IMarketBoard.cs +++ b/Dalamud/Plugin/Services/IMarketBoard.cs @@ -1,11 +1,11 @@ -using Dalamud.Game.Network.Structures; +using Dalamud.Game.Network.Structures; namespace Dalamud.Plugin.Services; /// /// Provides access to market board related events as the client receives/sends them. /// -public interface IMarketBoard +public interface IMarketBoard : IDalamudService { /// /// A delegate type used with the event. diff --git a/Dalamud/Plugin/Services/INamePlateGui.cs b/Dalamud/Plugin/Services/INamePlateGui.cs index eb2579bae..b58b5b7d0 100644 --- a/Dalamud/Plugin/Services/INamePlateGui.cs +++ b/Dalamud/Plugin/Services/INamePlateGui.cs @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// Class used to modify the data used when rendering nameplates. /// -public interface INamePlateGui +public interface INamePlateGui : IDalamudService { /// /// The delegate used for receiving nameplate update events. diff --git a/Dalamud/Plugin/Services/INotificationManager.cs b/Dalamud/Plugin/Services/INotificationManager.cs index 7d9ccd0b0..6d9dbf584 100644 --- a/Dalamud/Plugin/Services/INotificationManager.cs +++ b/Dalamud/Plugin/Services/INotificationManager.cs @@ -3,7 +3,7 @@ using Dalamud.Interface.ImGuiNotification; namespace Dalamud.Plugin.Services; /// Manager for notifications provided by Dalamud using ImGui. -public interface INotificationManager +public interface INotificationManager : IDalamudService { /// Adds a notification. /// The new notification. diff --git a/Dalamud/Plugin/Services/IObjectTable.cs b/Dalamud/Plugin/Services/IObjectTable.cs index 4c5305513..be8e50dea 100644 --- a/Dalamud/Plugin/Services/IObjectTable.cs +++ b/Dalamud/Plugin/Services/IObjectTable.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; +using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; namespace Dalamud.Plugin.Services; @@ -7,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// /// This collection represents the currently spawned FFXIV game objects. /// -public interface IObjectTable : IEnumerable +public interface IObjectTable : IDalamudService, IEnumerable { /// /// Gets the address of the object table. @@ -19,6 +20,11 @@ public interface IObjectTable : IEnumerable /// public int Length { get; } + /// + /// Gets the local player character, if one is present. + /// + public IPlayerCharacter? LocalPlayer { get; } + /// /// Gets an enumerator for accessing player objects. This will only contain BattleChara objects. /// Does not contain any mounts, minions, or accessories. diff --git a/Dalamud/Plugin/Services/IPartyFinderGui.cs b/Dalamud/Plugin/Services/IPartyFinderGui.cs index fb7a49acd..d9b14baed 100644 --- a/Dalamud/Plugin/Services/IPartyFinderGui.cs +++ b/Dalamud/Plugin/Services/IPartyFinderGui.cs @@ -1,11 +1,11 @@ -using Dalamud.Game.Gui.PartyFinder.Types; +using Dalamud.Game.Gui.PartyFinder.Types; namespace Dalamud.Plugin.Services; /// /// This class handles interacting with the native PartyFinder window. /// -public interface IPartyFinderGui +public interface IPartyFinderGui : IDalamudService { /// /// Event type fired each time the game receives an individual Party Finder listing. diff --git a/Dalamud/Plugin/Services/IPartyList.cs b/Dalamud/Plugin/Services/IPartyList.cs index b046f36db..1af3fa962 100644 --- a/Dalamud/Plugin/Services/IPartyList.cs +++ b/Dalamud/Plugin/Services/IPartyList.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Game.ClientState.Party; @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// This collection represents the actors present in your party or alliance. /// -public interface IPartyList : IReadOnlyCollection +public interface IPartyList : IDalamudService, IReadOnlyCollection { /// /// Gets the amount of party members the local player has. diff --git a/Dalamud/Plugin/Services/IPlayerState.cs b/Dalamud/Plugin/Services/IPlayerState.cs new file mode 100644 index 000000000..21d88010b --- /dev/null +++ b/Dalamud/Plugin/Services/IPlayerState.cs @@ -0,0 +1,243 @@ +using System.Collections.Generic; + +using Dalamud.Game.Player; + +using Lumina.Excel; +using Lumina.Excel.Sheets; + +namespace Dalamud.Plugin.Services; + +#pragma warning disable SA1400 // Access modifier should be declared: Interface members are public by default + +/// +/// Interface for determining the players state. +/// +public interface IPlayerState : IDalamudService +{ + /// + /// Gets a value indicating whether the local players data is loaded. + /// + /// + /// PlayerState is separate from , + /// and as such the game object might not exist when it's loaded. + /// + bool IsLoaded { get; } + + /// + /// Gets the name of the local character. + /// + string CharacterName { get; } + + /// + /// Gets the entity ID of the local character. + /// + uint EntityId { get; } + + /// + /// Gets the content ID of the local character. + /// + ulong ContentId { get; } + + /// + /// Gets the World row for the local character's current world. + /// + RowRef CurrentWorld { get; } + + /// + /// Gets the World row for the local character's home world. + /// + RowRef HomeWorld { get; } + + /// + /// Gets the sex of the local character. + /// + Sex Sex { get; } + + /// + /// Gets the Race row for the local character. + /// + RowRef Race { get; } + + /// + /// Gets the Tribe row for the local character. + /// + RowRef Tribe { get; } + + /// + /// Gets the ClassJob row for the local character's current class/job. + /// + RowRef ClassJob { get; } + + /// + /// Gets the current class/job's level of the local character. + /// + short Level { get; } + + /// + /// Gets a value indicating whether the local character's level is synced. + /// + bool IsLevelSynced { get; } + + /// + /// Gets the effective level of the local character, taking level sync into account. + /// + short EffectiveLevel { get; } + + /// + /// Gets the GuardianDeity row for the local character. + /// + RowRef GuardianDeity { get; } + + /// + /// Gets the birth month of the local character. + /// + byte BirthMonth { get; } + + /// + /// Gets the birth day of the local character. + /// + byte BirthDay { get; } + + /// + /// Gets the ClassJob row for the local character's starting class. + /// + RowRef FirstClass { get; } + + /// + /// Gets the Town row for the local character's starting town. + /// + RowRef StartTown { get; } + + /// + /// Gets the base strength of the local character. + /// + int BaseStrength { get; } + + /// + /// Gets the base dexterity of the local character. + /// + int BaseDexterity { get; } + + /// + /// Gets the base vitality of the local character. + /// + int BaseVitality { get; } + + /// + /// Gets the base intelligence of the local character. + /// + int BaseIntelligence { get; } + + /// + /// Gets the base mind of the local character. + /// + int BaseMind { get; } + + /// + /// Gets the piety mind of the local character. + /// + int BasePiety { get; } + + /// + /// Gets the GrandCompany row for the local character's current Grand Company affiliation. + /// + RowRef GrandCompany { get; } + + /// + /// Gets the Aetheryte row for the local player's home aetheryte. + /// + RowRef HomeAetheryte { get; } + + /// + /// Gets an array of Aetheryte rows for the local player's favourite aetherytes. + /// + IReadOnlyList> FavoriteAetherytes { get; } + + /// + /// Gets the Aetheryte row for the local player's free aetheryte. + /// + RowRef FreeAetheryte { get; } + + /// + /// Gets the amount of received player commendations of the local player. + /// + uint BaseRestedExperience { get; } + + /// + /// Gets the amount of received player commendations of the local player. + /// + short PlayerCommendations { get; } + + /// + /// Gets the Carrier Level of Delivery Moogle Quests of the local player. + /// + byte DeliveryLevel { get; } + + /// + /// Gets the mentor version of the local player. + /// + MentorVersion MentorVersion { get; } + + /// + /// Gets a value indicating whether the local player is any kind of Mentor (Battle or Trade Mentor). + /// + bool IsMentor { get; } + + /// + /// Gets a value indicating whether the local player is a Battle Mentor. + /// + bool IsBattleMentor { get; } + + /// + /// Gets a value indicating whether the local player is a Trade Mentor. + /// + bool IsTradeMentor { get; } + + /// + /// Gets a value indicating whether the local player is a novice (aka. Sprout or New Adventurer). + /// + /// + /// Can be if /nastatus was used to deactivate it. + /// + bool IsNovice { get; } + + /// + /// Gets a value indicating whether the local player is a returner. + /// + bool IsReturner { get; } + + /// + /// Gets the value of an attribute of the local character. + /// + /// The attribute to check. + /// The value of the specific attribute. + int GetAttribute(PlayerAttribute attribute); + + /// + /// Gets the Grand Company rank of the local character. + /// + /// The Grand Company to check. + /// The Grand Company rank of the local character. + byte GetGrandCompanyRank(GrandCompany grandCompany); + + /// + /// Gets the level of the local character's class/job. + /// + /// The ClassJob row to check. + /// The level of the requested class/job. + short GetClassJobLevel(ClassJob classJob); + + /// + /// Gets the experience of the local character's class/job. + /// + /// The ClassJob row to check. + /// The experience of the requested class/job. + int GetClassJobExperience(ClassJob classJob); + + /// + /// Gets the desynthesis level of the local character's crafter job. + /// + /// The ClassJob row to check. + /// The desynthesis level of the requested crafter job. + float GetDesynthesisLevel(ClassJob classJob); +} diff --git a/Dalamud/Plugin/Services/IPluginLog.cs b/Dalamud/Plugin/Services/IPluginLog.cs index 38406fd91..38786f0d2 100644 --- a/Dalamud/Plugin/Services/IPluginLog.cs +++ b/Dalamud/Plugin/Services/IPluginLog.cs @@ -1,4 +1,4 @@ -using Serilog; +using Serilog; using Serilog.Events; #pragma warning disable CS1573 // See https://github.com/dotnet/roslyn/issues/40325 @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// /// An opinionated service to handle logging for plugins. /// -public interface IPluginLog +public interface IPluginLog : IDalamudService { /// /// Gets a Serilog ILogger instance for this plugin. This is the entrypoint for plugins that wish to use more diff --git a/Dalamud/Plugin/Services/IReliableFileStorage.cs b/Dalamud/Plugin/Services/IReliableFileStorage.cs new file mode 100644 index 000000000..757493a20 --- /dev/null +++ b/Dalamud/Plugin/Services/IReliableFileStorage.cs @@ -0,0 +1,168 @@ +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +using Dalamud.Configuration; +using Dalamud.Storage; + +namespace Dalamud.Plugin.Services; + +/// +/// Service to interact with the file system, as a replacement for standard C# file I/O. +/// Writes and reads using this service are, to the best of our ability, atomic and reliable. +/// +/// All data is synced to disk immediately and written to a database, additionally to files on disk. This means +/// that in case of file corruption, data can likely be recovered from the database. +/// +/// However, this also means that operations using this service duplicate data on disk, so we don't recommend +/// performing large file operations. The service will not permit files larger than +/// (64MB) to be written. +/// +/// Saved configuration data using the class uses this functionality implicitly. +/// +[Experimental("Dalamud001")] +public interface IReliableFileStorage : IDalamudService +{ + /// + /// Gets the maximum file size, in bytes, that can be written using this service. + /// + /// + /// The service enforces this limit when writing files and fails with an appropriate exception + /// (for example or a custom exception) when a caller attempts to write + /// more than this number of bytes. + /// + long MaxFileSizeBytes { get; } + + /// + /// Check whether a file exists either on the local filesystem or in the transparent backup database. + /// + /// The file system path to check. Must not be null or empty. + /// + /// True if the file exists on disk or a backup copy exists in the storage's internal journal/backup database; + /// otherwise false. + /// + /// Thrown when is null or empty. + bool Exists(string path); + + /// + /// Write the given text into a file using UTF-8 encoding. The write is performed atomically and is persisted to + /// both the filesystem and the internal backup database used by this service. + /// + /// The file path to write to. Must not be null or empty. + /// The string contents to write. May be null, in which case an empty file is written. + /// A that completes when the write has finished and been flushed to disk and the backup. + /// Thrown when is null or empty. + Task WriteAllTextAsync(string path, string? contents); + + /// + /// Write the given text into a file using the provided . The write is performed + /// atomically (to the extent possible) and is persisted to both the filesystem and the internal backup database + /// used by this service. + /// + /// The file path to write to. Must not be null or empty. + /// The string contents to write. May be null, in which case an empty file is written. + /// The text encoding to use when serializing the string to bytes. Must not be null. + /// A that completes when the write has finished and been flushed to disk and the backup. + /// Thrown when is null or empty. + /// Thrown when is null. + Task WriteAllTextAsync(string path, string? contents, Encoding encoding); + + /// + /// Write the given bytes to a file. The write is persisted to both the filesystem and the service's internal + /// backup database. Avoid writing extremely large byte arrays because this service duplicates data on disk. + /// + /// The file path to write to. Must not be null or empty. + /// The raw bytes to write. Must not be null. + /// A that completes when the write has finished and been flushed to disk and the backup. + /// Thrown when is null or empty. + /// Thrown when is null. + Task WriteAllBytesAsync(string path, byte[] bytes); + + /// + /// Read all text from a file using UTF-8 encoding. If the file is unreadable or missing on disk, the service + /// attempts to return a backed-up copy from its internal journal/backup database. + /// + /// The file path to read. Must not be null or empty. + /// + /// When true the service prefers the internal backup database and returns backed-up contents if available. When + /// false the service tries the filesystem first and falls back to the backup only on error or when the file is missing. + /// + /// The textual contents of the file, decoded using UTF-8. + /// Thrown when is null or empty. + /// Thrown when the file does not exist on disk and no backup copy is available. + Task ReadAllTextAsync(string path, bool forceBackup = false); + + /// + /// Read all text from a file using the specified . If the file is unreadable or + /// missing on disk, the service attempts to return a backed-up copy from its internal journal/backup database. + /// + /// The file path to read. Must not be null or empty. + /// The encoding to use when decoding the stored bytes into text. Must not be null. + /// + /// When true the service prefers the internal backup database and returns backed-up contents if available. When + /// false the service tries the filesystem first and falls back to the backup only on error or when the file is missing. + /// + /// The textual contents of the file decoded using the provided . + /// Thrown when is null or empty. + /// Thrown when is null. + /// Thrown when the file does not exist on disk and no backup copy is available. + Task ReadAllTextAsync(string path, Encoding encoding, bool forceBackup = false); + + /// + /// Read all text from a file and invoke the provided callback with the string + /// contents. If the reader throws or the initial read fails, the service attempts a backup read and invokes the + /// reader again with the backup contents. If both reads fail the service surfaces an exception to the caller. + /// + /// The file path to read. Must not be null or empty. + /// + /// A callback invoked with the file's textual contents. Must not be null. + /// If the callback throws an exception the service treats that as a signal to retry the read using the + /// internal backup database and will invoke the callback again with the backup contents when available. + /// For example, the callback can throw when JSON deserialization fails to request the backup copy instead of + /// silently accepting corrupt data. + /// + /// A that completes when the read (and any attempted fallback) and callback invocation have finished. + /// Thrown when is null or empty. + /// Thrown when is null. + /// Thrown when the file does not exist on disk and no backup copy is available. + /// Thrown when both the filesystem read and the backup read fail for other reasons. + Task ReadAllTextAsync(string path, Action reader); + + /// + /// Read all text from a file using the specified and invoke the provided + /// callback with the decoded string contents. If the reader throws or the initial + /// read fails, the service attempts a backup read and invokes the reader again with the backup contents. If + /// both reads fail the service surfaces an exception to the caller. + /// + /// The file path to read. Must not be null or empty. + /// The encoding to use when decoding the stored bytes into text. Must not be null. + /// + /// A callback invoked with the file's textual contents. Must not be null. + /// If the callback throws an exception the service treats that as a signal to retry the read using the + /// internal backup database and will invoke the callback again with the backup contents when available. + /// For example, the callback can throw when JSON deserialization fails to request the backup copy instead of + /// silently accepting corrupt data. + /// + /// A that completes when the read (and any attempted fallback) and callback invocation have finished. + /// Thrown when is null or empty. + /// Thrown when or is null. + /// Thrown when the file does not exist on disk and no backup copy is available. + /// Thrown when both the filesystem read and the backup read fail for other reasons. + Task ReadAllTextAsync(string path, Encoding encoding, Action reader); + + /// + /// Read all bytes from a file. If the file is unreadable or missing on disk, the service may try to return a + /// backed-up copy from its internal journal/backup database. + /// + /// The file path to read. Must not be null or empty. + /// + /// When true the service prefers the internal backup database and returns the backed-up contents + /// if available. When false the service tries the filesystem first and falls back to the backup only + /// on error or when the file is missing. + /// + /// The raw bytes stored in the file. + /// Thrown when is null or empty. + /// Thrown when the file does not exist on disk and no backup copy is available. + Task ReadAllBytesAsync(string path, bool forceBackup = false); +} diff --git a/Dalamud/Plugin/Services/ISeStringEvaluator.cs b/Dalamud/Plugin/Services/ISeStringEvaluator.cs index 65932652e..8ab7adad1 100644 --- a/Dalamud/Plugin/Services/ISeStringEvaluator.cs +++ b/Dalamud/Plugin/Services/ISeStringEvaluator.cs @@ -9,7 +9,7 @@ namespace Dalamud.Plugin.Services; /// /// Defines a service for retrieving localized text for various in-game entities. /// -public interface ISeStringEvaluator +public interface ISeStringEvaluator : IDalamudService { /// /// Evaluates macros in a . @@ -38,6 +38,15 @@ public interface ISeStringEvaluator /// An evaluated . ReadOnlySeString EvaluateMacroString(string macroString, Span localParameters = default, ClientLanguage? language = null); + /// + /// Evaluates macros in a macro string. + /// + /// The macro string. + /// An optional list of local parameters. + /// An optional language override. + /// An evaluated . + ReadOnlySeString EvaluateMacroString(ReadOnlySpan macroString, Span localParameters = default, ClientLanguage? language = null); + /// /// Evaluates macros in text from the Addon sheet. /// diff --git a/Dalamud/Plugin/Services/ISigScanner.cs b/Dalamud/Plugin/Services/ISigScanner.cs index ac0f2c55f..fbbd8b05a 100644 --- a/Dalamud/Plugin/Services/ISigScanner.cs +++ b/Dalamud/Plugin/Services/ISigScanner.cs @@ -2,12 +2,14 @@ using System.Collections.Generic; using System.Diagnostics; using System.Threading; +using Dalamud.Plugin.Services; + namespace Dalamud.Game; /// /// A SigScanner facilitates searching for memory signatures in a given ProcessModule. /// -public interface ISigScanner +public interface ISigScanner : IDalamudService { /// /// Gets a value indicating whether the search on this module is performed on a copy. diff --git a/Dalamud/Plugin/Services/ITargetManager.cs b/Dalamud/Plugin/Services/ITargetManager.cs index 5ba9f390e..9c9fce550 100644 --- a/Dalamud/Plugin/Services/ITargetManager.cs +++ b/Dalamud/Plugin/Services/ITargetManager.cs @@ -1,11 +1,12 @@ -using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Plugin.Services; namespace Dalamud.Game.ClientState.Objects; /// /// Get and set various kinds of targets for the player. /// -public interface ITargetManager +public interface ITargetManager : IDalamudService { /// /// Gets or sets the current target. diff --git a/Dalamud/Plugin/Services/ITextureProvider.cs b/Dalamud/Plugin/Services/ITextureProvider.cs index 9c499d3f5..dc0522aa8 100644 --- a/Dalamud/Plugin/Services/ITextureProvider.cs +++ b/Dalamud/Plugin/Services/ITextureProvider.cs @@ -33,7 +33,7 @@ namespace Dalamud.Plugin.Services; /// . /// /// -public interface ITextureProvider +public interface ITextureProvider : IDalamudService { /// Creates an empty texture. /// Texture specifications. diff --git a/Dalamud/Plugin/Services/ITextureReadbackProvider.cs b/Dalamud/Plugin/Services/ITextureReadbackProvider.cs index 3d2894355..00b684cbb 100644 --- a/Dalamud/Plugin/Services/ITextureReadbackProvider.cs +++ b/Dalamud/Plugin/Services/ITextureReadbackProvider.cs @@ -3,14 +3,13 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Dalamud.Interface.Internal; using Dalamud.Interface.Textures; using Dalamud.Interface.Textures.TextureWraps; namespace Dalamud.Plugin.Services; /// Service that grants you to read instances of . -public interface ITextureReadbackProvider +public interface ITextureReadbackProvider : IDalamudService { /// Gets the raw data of a texture wrap. /// The source texture wrap. diff --git a/Dalamud/Plugin/Services/ITextureSubstitutionProvider.cs b/Dalamud/Plugin/Services/ITextureSubstitutionProvider.cs index 371fbaf0f..dcd1b00cc 100644 --- a/Dalamud/Plugin/Services/ITextureSubstitutionProvider.cs +++ b/Dalamud/Plugin/Services/ITextureSubstitutionProvider.cs @@ -1,11 +1,11 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Dalamud.Plugin.Services; /// /// Service that grants you the ability to replace texture data that is to be loaded by Dalamud. /// -public interface ITextureSubstitutionProvider +public interface ITextureSubstitutionProvider : IDalamudService { /// /// Delegate describing a function that may be used to intercept and replace texture data. diff --git a/Dalamud/Plugin/Services/ITitleScreenMenu.cs b/Dalamud/Plugin/Services/ITitleScreenMenu.cs index 9f7b17ea3..50bae62a1 100644 --- a/Dalamud/Plugin/Services/ITitleScreenMenu.cs +++ b/Dalamud/Plugin/Services/ITitleScreenMenu.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Dalamud.Interface; using Dalamud.Interface.Textures; @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// /// Interface for class responsible for managing elements in the title screen menu. /// -public interface ITitleScreenMenu +public interface ITitleScreenMenu : IDalamudService { /// /// Gets the list of read only entries in the title screen menu. diff --git a/Dalamud/Plugin/Services/IToastGui.cs b/Dalamud/Plugin/Services/IToastGui.cs index ef83e95ac..c472cbb1f 100644 --- a/Dalamud/Plugin/Services/IToastGui.cs +++ b/Dalamud/Plugin/Services/IToastGui.cs @@ -1,4 +1,4 @@ -using Dalamud.Game.Gui.Toast; +using Dalamud.Game.Gui.Toast; using Dalamud.Game.Text.SeStringHandling; namespace Dalamud.Plugin.Services; @@ -6,7 +6,7 @@ namespace Dalamud.Plugin.Services; /// /// This class facilitates interacting with and creating native toast windows. /// -public interface IToastGui +public interface IToastGui : IDalamudService { /// /// A delegate type used when a normal toast window appears. diff --git a/Dalamud/Plugin/Services/IUnlockState.cs b/Dalamud/Plugin/Services/IUnlockState.cs new file mode 100644 index 000000000..0409843c4 --- /dev/null +++ b/Dalamud/Plugin/Services/IUnlockState.cs @@ -0,0 +1,328 @@ +using System.Diagnostics.CodeAnalysis; + +using Lumina.Excel; +using Lumina.Excel.Sheets; + +namespace Dalamud.Plugin.Services; + +#pragma warning disable SA1400 // Access modifier should be declared: Interface members are public by default + +/// +/// Interface for determining unlock state of various content in the game. +/// +[Experimental("Dalamud001")] +public interface IUnlockState : IDalamudService +{ + /// + /// A delegate type used for the event. + /// + /// A RowRef of the unlocked thing. + delegate void UnlockDelegate(RowRef rowRef); + + /// + /// Event triggered when something was unlocked. + /// + event UnlockDelegate? Unlock; + + /// + /// Determines whether the specified Action is unlocked. + /// + /// The Action row to check. + /// if unlocked; otherwise, . + bool IsActionUnlocked(Lumina.Excel.Sheets.Action row); + + /// + /// Determines whether the specified AetherCurrentCompFlgSet is unlocked. + /// + /// The AetherCurrentCompFlgSet row to check. + /// if unlocked; otherwise, . + bool IsAetherCurrentCompFlgSetUnlocked(AetherCurrentCompFlgSet row); + + /// + /// Determines whether the specified AetherCurrent is unlocked. + /// + /// The AetherCurrent row to check. + /// if unlocked; otherwise, . + bool IsAetherCurrentUnlocked(AetherCurrent row); + + /// + /// Determines whether the specified AozAction (Blue Mage Action) is unlocked. + /// + /// The AozAction row to check. + /// if unlocked; otherwise, . + bool IsAozActionUnlocked(AozAction row); + + /// + /// Determines whether the specified BannerBg (Portrait Backgrounds) is unlocked. + /// + /// The BannerBg row to check. + /// if unlocked; otherwise, . + bool IsBannerBgUnlocked(BannerBg row); + + /// + /// Determines whether the specified BannerCondition is unlocked. + /// + /// The BannerCondition row to check. + /// if unlocked; otherwise, . + bool IsBannerConditionUnlocked(BannerCondition row); + + /// + /// Determines whether the specified BannerDecoration (Portrait Accents) is unlocked. + /// + /// The BannerDecoration row to check. + /// if unlocked; otherwise, . + bool IsBannerDecorationUnlocked(BannerDecoration row); + + /// + /// Determines whether the specified BannerFacial (Portrait Expressions) is unlocked. + /// + /// The BannerFacial row to check. + /// if unlocked; otherwise, . + bool IsBannerFacialUnlocked(BannerFacial row); + + /// + /// Determines whether the specified BannerFrame (Portrait Frames) is unlocked. + /// + /// The BannerFrame row to check. + /// if unlocked; otherwise, . + bool IsBannerFrameUnlocked(BannerFrame row); + + /// + /// Determines whether the specified BannerTimeline (Portrait Poses) is unlocked. + /// + /// The BannerTimeline row to check. + /// if unlocked; otherwise, . + bool IsBannerTimelineUnlocked(BannerTimeline row); + + /// + /// Determines whether the specified BuddyAction (Action of the players Chocobo Companion) is unlocked. + /// + /// The BuddyAction row to check. + /// if unlocked; otherwise, . + bool IsBuddyActionUnlocked(BuddyAction row); + + /// + /// Determines whether the specified BuddyEquip (Equipment of the players Chocobo Companion) is unlocked. + /// + /// The BuddyEquip row to check. + /// if unlocked; otherwise, . + bool IsBuddyEquipUnlocked(BuddyEquip row); + + /// + /// Determines whether the specified CharaMakeCustomize (Hairstyles and Face Paint patterns) is unlocked. + /// + /// The CharaMakeCustomize row to check. + /// if unlocked; otherwise, . + bool IsCharaMakeCustomizeUnlocked(CharaMakeCustomize row); + + /// + /// Determines whether the specified ChocoboTaxiStand (Chocobokeeps of the Chocobo Porter service) is unlocked. + /// + /// The ChocoboTaxiStand row to check. + /// if unlocked; otherwise, . + bool IsChocoboTaxiStandUnlocked(ChocoboTaxiStand row); + + /// + /// Determines whether the specified Companion (Minions) is unlocked. + /// + /// The Companion row to check. + /// if unlocked; otherwise, . + bool IsCompanionUnlocked(Companion row); + + /// + /// Determines whether the specified CraftAction is unlocked. + /// + /// The CraftAction row to check. + /// if unlocked; otherwise, . + bool IsCraftActionUnlocked(CraftAction row); + + /// + /// Determines whether the specified CSBonusContentType is unlocked. + /// + /// The CSBonusContentType row to check. + /// if unlocked; otherwise, . + bool IsCSBonusContentTypeUnlocked(CSBonusContentType row); + + /// + /// Determines whether the specified Emote is unlocked. + /// + /// The Emote row to check. + /// if unlocked; otherwise, . + bool IsEmoteUnlocked(Emote row); + + /// + /// Determines whether the specified EmjVoiceNpc (Doman Mahjong Characters) is unlocked. + /// + /// The EmjVoiceNpc row to check. + /// if unlocked; otherwise, . + bool IsEmjVoiceNpcUnlocked(EmjVoiceNpc row); + + /// + /// Determines whether the specified EmjCostume (Doman Mahjong Character Costume) is unlocked. + /// + /// The EmjCostume row to check. + /// if unlocked; otherwise, . + bool IsEmjCostumeUnlocked(EmjCostume row); + + /// + /// Determines whether the specified GeneralAction is unlocked. + /// + /// The GeneralAction row to check. + /// if unlocked; otherwise, . + bool IsGeneralActionUnlocked(GeneralAction row); + + /// + /// Determines whether the specified Glasses is unlocked. + /// + /// The Glasses row to check. + /// if unlocked; otherwise, . + bool IsGlassesUnlocked(Glasses row); + + /// + /// Determines whether the specified HowTo is unlocked. + /// + /// The HowTo row to check. + /// if unlocked; otherwise, . + bool IsHowToUnlocked(HowTo row); + + /// + /// Determines whether the specified InstanceContent is unlocked. + /// + /// The InstanceContent row to check. + /// if unlocked; otherwise, . + bool IsInstanceContentUnlocked(InstanceContent row); + + /// + /// Determines whether the specified Item is considered unlockable. + /// + /// The Item row to check. + /// if unlockable; otherwise, . + bool IsItemUnlockable(Item row); + + /// + /// Determines whether the specified Item is unlocked. + /// + /// The Item row to check. + /// if unlocked; otherwise, . + bool IsItemUnlocked(Item row); + + /// + /// Determines whether the specified McGuffin is unlocked. + /// + /// The McGuffin row to check. + /// if unlocked; otherwise, . + bool IsMcGuffinUnlocked(McGuffin row); + + /// + /// Determines whether the specified MJILandmark (Island Sanctuary landmark) is unlocked. + /// + /// The MJILandmark row to check. + /// if unlocked; otherwise, . + bool IsMJILandmarkUnlocked(MJILandmark row); + + /// + /// Determines whether the specified MKDLore (Occult Record) is unlocked. + /// + /// The MKDLore row to check. + /// if unlocked; otherwise, . + bool IsMKDLoreUnlocked(MKDLore row); + + /// + /// Determines whether the specified Mount is unlocked. + /// + /// The Mount row to check. + /// if unlocked; otherwise, . + bool IsMountUnlocked(Mount row); + + /// + /// Determines whether the specified NotebookDivision (Categories in Crafting/Gathering Log) is unlocked. + /// + /// The NotebookDivision row to check. + /// if unlocked; otherwise, . + bool IsNotebookDivisionUnlocked(NotebookDivision row); + + /// + /// Determines whether the specified Orchestrion roll is unlocked. + /// + /// The Orchestrion row to check. + /// if unlocked; otherwise, . + bool IsOrchestrionUnlocked(Orchestrion row); + + /// + /// Determines whether the specified Ornament (Fashion Accessories) is unlocked. + /// + /// The Ornament row to check. + /// if unlocked; otherwise, . + bool IsOrnamentUnlocked(Ornament row); + + /// + /// Determines whether the specified Perform (Performance Instruments) is unlocked. + /// + /// The Perform row to check. + /// if unlocked; otherwise, . + bool IsPerformUnlocked(Perform row); + + /// + /// Determines whether the specified PublicContent is unlocked. + /// + /// The PublicContent row to check. + /// if unlocked; otherwise, . + bool IsPublicContentUnlocked(PublicContent row); + + /// + /// Determines whether the specified Recipe is unlocked. + /// + /// The Recipe row to check. + /// if unlocked; otherwise, . + bool IsRecipeUnlocked(Recipe row); + + /// + /// Determines whether the underlying RowRef type is unlocked. + /// + /// The RowRef to check. + /// if unlocked; otherwise, . + bool IsRowRefUnlocked(RowRef rowRef); + + /// + /// Determines whether the underlying RowRef type is unlocked. + /// + /// The type of the Excel row. + /// The RowRef to check. + /// if unlocked; otherwise, . + bool IsRowRefUnlocked(RowRef rowRef) where T : struct, IExcelRow; + + /// + /// Determines whether the specified SecretRecipeBook (Master Recipe Books) is unlocked. + /// + /// The SecretRecipeBook row to check. + /// if unlocked; otherwise, . + bool IsSecretRecipeBookUnlocked(SecretRecipeBook row); + + /// + /// Determines whether the specified Trait is unlocked. + /// + /// The Trait row to check. + /// if unlocked; otherwise, . + bool IsTraitUnlocked(Trait row); + + /// + /// Determines whether the specified TripleTriadCard is unlocked. + /// + /// The TripleTriadCard row to check. + /// if unlocked; otherwise, . + bool IsTripleTriadCardUnlocked(TripleTriadCard row); + + /// + /// Determines whether the specified unlock link is unlocked or quest is completed. + /// + /// The unlock link id or quest id (quest ids in this case are over 65536). + /// if unlocked; otherwise, . + bool IsUnlockLinkUnlocked(uint unlockLink); + + /// + /// Determines whether the specified unlock link is unlocked. + /// + /// The unlock link id. + /// if unlocked; otherwise, . + bool IsUnlockLinkUnlocked(ushort unlockLink); +} diff --git a/Dalamud/Service/ServiceManager.cs b/Dalamud/Service/ServiceManager.cs index 9847f7147..88c6366fd 100644 --- a/Dalamud/Service/ServiceManager.cs +++ b/Dalamud/Service/ServiceManager.cs @@ -9,11 +9,14 @@ using System.Threading.Tasks; using Dalamud.Configuration.Internal; using Dalamud.Game; +using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Logging.Internal; +using Dalamud.Plugin.Services; using Dalamud.Storage; using Dalamud.Utility; using Dalamud.Utility.Timing; + using JetBrains.Annotations; // API10 TODO: Move to Dalamud.Service namespace. Some plugins reflect this... including my own, oops. There's a todo @@ -541,9 +544,11 @@ internal static class ServiceManager if (attr == null) return ServiceKind.None; - Debug.Assert( - type.IsAssignableTo(typeof(IServiceType)), - "Service did not inherit from IServiceType"); + if (!type.IsAssignableTo(typeof(IServiceType))) + { + Log.Error($"Service {type.Name} did not inherit from IServiceType"); + Debug.Fail("Service did not inherit from IServiceType"); + } if (attr.IsAssignableTo(typeof(BlockingEarlyLoadedServiceAttribute))) return ServiceKind.BlockingEarlyLoadedService; @@ -552,7 +557,16 @@ internal static class ServiceManager return ServiceKind.EarlyLoadedService; if (attr.IsAssignableTo(typeof(ScopedServiceAttribute))) + { + if (type.GetCustomAttribute() != null + && !type.IsAssignableTo(typeof(IDalamudService))) + { + Log.Error($"Plugin-scoped service {type.Name} must inherit from IDalamudService"); + Debug.Fail("Plugin-scoped service must inherit from IDalamudService"); + } + return ServiceKind.ScopedService; + } return ServiceKind.ProvidedService; } diff --git a/Dalamud/Storage/ReliableFileStorage.cs b/Dalamud/Storage/ReliableFileStorage.cs index 9791b9e45..d9f8526c3 100644 --- a/Dalamud/Storage/ReliableFileStorage.cs +++ b/Dalamud/Storage/ReliableFileStorage.cs @@ -1,5 +1,6 @@ using System.IO; using System.Text; +using System.Threading.Tasks; using Dalamud.Logging.Internal; using Dalamud.Utility; @@ -92,8 +93,9 @@ internal class ReliableFileStorage : IInternalDisposableService /// Path to write to. /// The contents of the file. /// Container to write to. - public void WriteAllText(string path, string? contents, Guid containerId = default) - => this.WriteAllText(path, contents, Encoding.UTF8, containerId); + /// A representing the asynchronous operation. + public async Task WriteAllTextAsync(string path, string? contents, Guid containerId = default) + => await this.WriteAllTextAsync(path, contents, Encoding.UTF8, containerId); /// /// Write all text to a file. @@ -102,10 +104,11 @@ internal class ReliableFileStorage : IInternalDisposableService /// The contents of the file. /// The encoding to write with. /// Container to write to. - public void WriteAllText(string path, string? contents, Encoding encoding, Guid containerId = default) + /// A representing the asynchronous operation. + public async Task WriteAllTextAsync(string path, string? contents, Encoding encoding, Guid containerId = default) { var bytes = encoding.GetBytes(contents ?? string.Empty); - this.WriteAllBytes(path, bytes, containerId); + await this.WriteAllBytesAsync(path, bytes, containerId); } /// @@ -114,7 +117,8 @@ internal class ReliableFileStorage : IInternalDisposableService /// Path to write to. /// The contents of the file. /// Container to write to. - public void WriteAllBytes(string path, byte[] bytes, Guid containerId = default) + /// A representing the asynchronous operation. + public Task WriteAllBytesAsync(string path, byte[] bytes, Guid containerId = default) { ArgumentException.ThrowIfNullOrEmpty(path); @@ -123,7 +127,7 @@ internal class ReliableFileStorage : IInternalDisposableService if (this.db == null) { FilesystemUtil.WriteAllBytesSafe(path, bytes); - return; + return Task.CompletedTask; } this.db.RunInTransaction(() => @@ -149,6 +153,8 @@ internal class ReliableFileStorage : IInternalDisposableService FilesystemUtil.WriteAllBytesSafe(path, bytes); }); } + + return Task.CompletedTask; } /// @@ -161,8 +167,8 @@ internal class ReliableFileStorage : IInternalDisposableService /// The container to read from. /// All text stored in this file. /// Thrown if the file does not exist on the filesystem or in the backup. - public string ReadAllText(string path, bool forceBackup = false, Guid containerId = default) - => this.ReadAllText(path, Encoding.UTF8, forceBackup, containerId); + public Task ReadAllTextAsync(string path, bool forceBackup = false, Guid containerId = default) + => this.ReadAllTextAsync(path, Encoding.UTF8, forceBackup, containerId); /// /// Read all text from a file. @@ -175,9 +181,9 @@ internal class ReliableFileStorage : IInternalDisposableService /// The container to read from. /// All text stored in this file. /// Thrown if the file does not exist on the filesystem or in the backup. - public string ReadAllText(string path, Encoding encoding, bool forceBackup = false, Guid containerId = default) + public async Task ReadAllTextAsync(string path, Encoding encoding, bool forceBackup = false, Guid containerId = default) { - var bytes = this.ReadAllBytes(path, forceBackup, containerId); + var bytes = await this.ReadAllBytesAsync(path, forceBackup, containerId); return encoding.GetString(bytes); } @@ -191,8 +197,9 @@ internal class ReliableFileStorage : IInternalDisposableService /// The container to read from. /// Thrown if the file does not exist on the filesystem or in the backup. /// Thrown here if the file and the backup fail their read. - public void ReadAllText(string path, Action reader, Guid containerId = default) - => this.ReadAllText(path, Encoding.UTF8, reader, containerId); + /// A representing the asynchronous operation. + public async Task ReadAllTextAsync(string path, Action reader, Guid containerId = default) + => await this.ReadAllTextAsync(path, Encoding.UTF8, reader, containerId); /// /// Read all text from a file, and automatically try again with the backup if the file does not exist or @@ -205,7 +212,8 @@ internal class ReliableFileStorage : IInternalDisposableService /// The container to read from. /// Thrown if the file does not exist on the filesystem or in the backup. /// Thrown here if the file and the backup fail their read. - public void ReadAllText(string path, Encoding encoding, Action reader, Guid containerId = default) + /// A representing the asynchronous operation. + public async Task ReadAllTextAsync(string path, Encoding encoding, Action reader, Guid containerId = default) { ArgumentException.ThrowIfNullOrEmpty(path); @@ -216,7 +224,7 @@ internal class ReliableFileStorage : IInternalDisposableService // 1.) Try without using the backup try { - var text = this.ReadAllText(path, encoding, false, containerId); + var text = await this.ReadAllTextAsync(path, encoding, false, containerId); reader(text); return; } @@ -233,7 +241,7 @@ internal class ReliableFileStorage : IInternalDisposableService // 2.) Try using the backup try { - var text = this.ReadAllText(path, encoding, true, containerId); + var text = await this.ReadAllTextAsync(path, encoding, true, containerId); reader(text); } catch (Exception ex) @@ -253,7 +261,7 @@ internal class ReliableFileStorage : IInternalDisposableService /// The container to read from. /// All bytes stored in this file. /// Thrown if the file does not exist on the filesystem or in the backup. - public byte[] ReadAllBytes(string path, bool forceBackup = false, Guid containerId = default) + public async Task ReadAllBytesAsync(string path, bool forceBackup = false, Guid containerId = default) { ArgumentException.ThrowIfNullOrEmpty(path); @@ -265,15 +273,12 @@ internal class ReliableFileStorage : IInternalDisposableService var normalizedPath = NormalizePath(path); var file = this.db.Table().FirstOrDefault(f => f.Path == normalizedPath && f.ContainerId == containerId); - if (file == null) - throw new FileNotFoundException(); - - return file.Data; + return file == null ? throw new FileNotFoundException() : file.Data; } // If the file doesn't exist, immediately check the backup db if (!File.Exists(path)) - return this.ReadAllBytes(path, true, containerId); + return await this.ReadAllBytesAsync(path, true, containerId); try { @@ -282,7 +287,7 @@ internal class ReliableFileStorage : IInternalDisposableService catch (Exception e) { Log.Error(e, "Failed to read file from disk, falling back to database"); - return this.ReadAllBytes(path, true, containerId); + return await this.ReadAllBytesAsync(path, true, containerId); } } diff --git a/Dalamud/Storage/ReliableFileStoragePluginScoped.cs b/Dalamud/Storage/ReliableFileStoragePluginScoped.cs new file mode 100644 index 000000000..59d6bccc4 --- /dev/null +++ b/Dalamud/Storage/ReliableFileStoragePluginScoped.cs @@ -0,0 +1,199 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +using Dalamud.IoC; +using Dalamud.IoC.Internal; +using Dalamud.Plugin.Internal.Types; +using Dalamud.Plugin.Services; + +namespace Dalamud.Storage; + +#pragma warning disable Dalamud001 + +/// +/// Plugin-scoped VFS wrapper. +/// +[PluginInterface] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +public class ReliableFileStoragePluginScoped : IReliableFileStorage, IInternalDisposableService +{ + private readonly Lock pendingLock = new(); + private readonly HashSet pendingWrites = []; + + private readonly LocalPlugin plugin; + + [ServiceManager.ServiceDependency] + private readonly ReliableFileStorage storage = Service.Get(); + + // When true, the scope is disposing and new write requests are rejected. + private volatile bool isDisposing = false; + + /// + /// Initializes a new instance of the class. + /// + /// The owner plugin. + [ServiceManager.ServiceConstructor] + internal ReliableFileStoragePluginScoped(LocalPlugin plugin) + { + this.plugin = plugin; + } + + /// + public long MaxFileSizeBytes => 64 * 1024 * 1024; + + /// + public bool Exists(string path) + { + if (this.isDisposing) + throw new ObjectDisposedException(nameof(ReliableFileStoragePluginScoped)); + + return this.storage.Exists(path, this.plugin.EffectiveWorkingPluginId); + } + + /// + public Task WriteAllTextAsync(string path, string? contents) + { + // Route through WriteAllBytesAsync so all write tracking and size checks are centralized. + ArgumentException.ThrowIfNullOrEmpty(path); + + var bytes = Encoding.UTF8.GetBytes(contents ?? string.Empty); + return this.WriteAllBytesAsync(path, bytes); + } + + /// + public Task WriteAllTextAsync(string path, string? contents, Encoding encoding) + { + // Route through WriteAllBytesAsync so all write tracking and size checks are centralized. + ArgumentException.ThrowIfNullOrEmpty(path); + ArgumentNullException.ThrowIfNull(encoding); + + var bytes = encoding.GetBytes(contents ?? string.Empty); + return this.WriteAllBytesAsync(path, bytes); + } + + /// + public Task WriteAllBytesAsync(string path, byte[] bytes) + { + ArgumentException.ThrowIfNullOrEmpty(path); + ArgumentNullException.ThrowIfNull(bytes); + + if (bytes.LongLength > this.MaxFileSizeBytes) + throw new ArgumentException($"The provided data exceeds the maximum allowed size of {this.MaxFileSizeBytes} bytes.", nameof(bytes)); + + // Start the underlying write task + var task = Task.Run(() => this.storage.WriteAllBytesAsync(path, bytes, this.plugin.EffectiveWorkingPluginId)); + + // Track the task so we can wait for it on dispose + lock (this.pendingLock) + { + if (this.isDisposing) + throw new ObjectDisposedException(nameof(ReliableFileStoragePluginScoped)); + + this.pendingWrites.Add(task); + } + + // Remove when done, if the task is already done this runs synchronously here and removes immediately + _ = task.ContinueWith(t => + { + lock (this.pendingLock) + { + this.pendingWrites.Remove(t); + } + }, TaskContinuationOptions.ExecuteSynchronously); + + return task; + } + + /// + public Task ReadAllTextAsync(string path, bool forceBackup = false) + { + if (this.isDisposing) + throw new ObjectDisposedException(nameof(ReliableFileStoragePluginScoped)); + + ArgumentException.ThrowIfNullOrEmpty(path); + + return this.storage.ReadAllTextAsync(path, forceBackup, this.plugin.EffectiveWorkingPluginId); + } + + /// + public Task ReadAllTextAsync(string path, Encoding encoding, bool forceBackup = false) + { + if (this.isDisposing) + throw new ObjectDisposedException(nameof(ReliableFileStoragePluginScoped)); + + ArgumentException.ThrowIfNullOrEmpty(path); + ArgumentNullException.ThrowIfNull(encoding); + + return this.storage.ReadAllTextAsync(path, encoding, forceBackup, this.plugin.EffectiveWorkingPluginId); + } + + /// + public Task ReadAllTextAsync(string path, Action reader) + { + if (this.isDisposing) + throw new ObjectDisposedException(nameof(ReliableFileStoragePluginScoped)); + + ArgumentException.ThrowIfNullOrEmpty(path); + ArgumentNullException.ThrowIfNull(reader); + + return this.storage.ReadAllTextAsync(path, reader, this.plugin.EffectiveWorkingPluginId); + } + + /// + public Task ReadAllTextAsync(string path, Encoding encoding, Action reader) + { + if (this.isDisposing) + throw new ObjectDisposedException(nameof(ReliableFileStoragePluginScoped)); + + ArgumentException.ThrowIfNullOrEmpty(path); + ArgumentNullException.ThrowIfNull(encoding); + ArgumentNullException.ThrowIfNull(reader); + + return this.storage.ReadAllTextAsync(path, encoding, reader, this.plugin.EffectiveWorkingPluginId); + } + + /// + public Task ReadAllBytesAsync(string path, bool forceBackup = false) + { + if (this.isDisposing) + throw new ObjectDisposedException(nameof(ReliableFileStoragePluginScoped)); + + ArgumentException.ThrowIfNullOrEmpty(path); + + return this.storage.ReadAllBytesAsync(path, forceBackup, this.plugin.EffectiveWorkingPluginId); + } + + /// + public void DisposeService() + { + Task[] tasksSnapshot; + lock (this.pendingLock) + { + // Mark disposing to reject new writes. + this.isDisposing = true; + + if (this.pendingWrites.Count == 0) + return; + + tasksSnapshot = this.pendingWrites.ToArray(); + } + + try + { + // Wait for all pending writes to complete. If some complete while we're waiting they will be in tasksSnapshot + // and are observed here; newly started writes are rejected due to isDisposing. + Task.WaitAll(tasksSnapshot); + } + catch (AggregateException) + { + // Swallow exceptions here: the underlying write failures will have been surfaced earlier to callers. + // We don't want dispose to throw and crash unload sequences. + } + } +} diff --git a/Dalamud/Support/BugBait.cs b/Dalamud/Support/BugBait.cs index 8dbf2e429..7ce96208c 100644 --- a/Dalamud/Support/BugBait.cs +++ b/Dalamud/Support/BugBait.cs @@ -36,6 +36,7 @@ internal static class BugBait Reporter = reporter, Name = plugin.InternalName, Version = isTesting ? plugin.TestingAssemblyVersion?.ToString() : plugin.AssemblyVersion.ToString(), + Platform = Util.GetHostPlatform().ToString(), DalamudHash = Util.GetScmVersion(), }; @@ -66,6 +67,9 @@ internal static class BugBait [JsonProperty("version")] public string? Version { get; set; } + [JsonProperty("platform")] + public string? Platform { get; set; } + [JsonProperty("reporter")] public string? Reporter { get; set; } diff --git a/Dalamud/Support/DalamudReleases.cs b/Dalamud/Support/DalamudReleases.cs index 15e851da2..603c77487 100644 --- a/Dalamud/Support/DalamudReleases.cs +++ b/Dalamud/Support/DalamudReleases.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Dalamud.Configuration.Internal; using Dalamud.Networking.Http; +using Dalamud.Utility; using Newtonsoft.Json; @@ -15,7 +16,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; @@ -30,20 +31,24 @@ internal class DalamudReleases : IServiceType this.httpClient = httpClient; this.config = config; } - + /// /// Get the latest version info for the current track. /// /// The version info for the current track. - public async Task GetVersionForCurrentTrack() + public async Task GetVersionForCurrentTrack() { - var url = string.Format(VersionInfoUrl, [this.config.DalamudBetaKind]); + var currentTrack = Util.GetActiveTrack(); + if (currentTrack.IsNullOrEmpty()) + return null; + + var url = string.Format(VersionInfoUrl, [currentTrack]); var response = await this.httpClient.SharedHttpClient.GetAsync(url); response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject(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 4af8d5ffc..88048c462 100644 --- a/Dalamud/Support/Troubleshooting.cs +++ b/Dalamud/Support/Troubleshooting.cs @@ -73,7 +73,7 @@ public static class Troubleshooting DalamudGitHash = Util.GetGitHash() ?? "Unknown", GameVersion = startInfo.GameVersion?.ToString() ?? "Unknown", Language = startInfo.Language.ToString(), - BetaKey = configuration.DalamudBetaKey, + BetaKey = Util.GetActiveTrack(), DoPluginTest = configuration.DoPluginTest, LoadAllApiLevels = pluginManager?.LoadAllApiLevels == true, InterfaceLoaded = interfaceManager?.IsReady ?? false, diff --git a/Dalamud/Utility/Internal/LazyLoc.cs b/Dalamud/Utility/Internal/LazyLoc.cs new file mode 100644 index 000000000..08ea6bd95 --- /dev/null +++ b/Dalamud/Utility/Internal/LazyLoc.cs @@ -0,0 +1,31 @@ +using CheapLoc; + +using Dalamud.Bindings.ImGui; + +namespace Dalamud.Utility.Internal; + +/// +/// Represents a lazily localized string, consisting of a localization key and a fallback value. +/// +/// The localization key used to retrieve the localized value. +/// The fallback text to use if the localization key is not found. +internal readonly record struct LazyLoc(string Key, string Fallback) +{ + public static implicit operator ImU8String(LazyLoc locRef) + => new(locRef.ToString()); + + /// + /// Creates a new instance of with the specified localization key and fallback text. + /// + /// The localization key used to retrieve the localized value. + /// The fallback text to use if the localization key is not found. + /// A instance representing the localized value. + public static LazyLoc Localize(string key, string fallback) + => new(key, fallback); + + /// + public override string ToString() + { + return Loc.Localize(this.Key, this.Fallback); + } +} diff --git a/Dalamud/Utility/RentedSeStringBuilder.cs b/Dalamud/Utility/RentedSeStringBuilder.cs new file mode 100644 index 000000000..93a45d967 --- /dev/null +++ b/Dalamud/Utility/RentedSeStringBuilder.cs @@ -0,0 +1,19 @@ +using Lumina.Text; + +namespace Dalamud.Utility; + +/// +/// Provides a temporarily rented from a shared pool. +/// +public readonly struct RentedSeStringBuilder() : IDisposable +{ + /// + /// Gets the rented value from the shared pool. + /// + public SeStringBuilder Builder { get; } = SeStringBuilder.SharedPool.Get(); + + /// + /// Returns the rented to the shared pool. + /// + public void Dispose() => SeStringBuilder.SharedPool.Return(this.Builder); +} diff --git a/Dalamud/Utility/SeStringExtensions.cs b/Dalamud/Utility/SeStringExtensions.cs index 904375250..cd095c467 100644 --- a/Dalamud/Utility/SeStringExtensions.cs +++ b/Dalamud/Utility/SeStringExtensions.cs @@ -24,6 +24,7 @@ public static class SeStringExtensions /// /// The original Lumina SeString. /// The re-parsed Dalamud SeString. + [Obsolete("Switch to using ReadOnlySeString instead of Lumina's SeString.", true)] public static DSeString ToDalamudString(this LSeString originalString) => DSeString.Parse(originalString.RawData); /// diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index a95f3ed66..19610ef64 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -9,6 +9,7 @@ using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; +using System.Threading; using System.Threading.Tasks; using Dalamud.Bindings.ImGui; @@ -17,12 +18,17 @@ using Dalamud.Game; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Interface.Colors; +using Dalamud.Interface.Internal; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Support; + using Lumina.Excel.Sheets; + using Serilog; + using TerraFX.Interop.Windows; + using Windows.Win32.System.Memory; using Windows.Win32.System.Ole; using Windows.Win32.UI.WindowsAndMessaging; @@ -65,6 +71,7 @@ public static partial class Util private static string? scmVersionInternal; private static string? gitHashInternal; private static string? gitHashClientStructsInternal; + private static string? branchInternal; private static ulong moduleStartAddr; private static ulong moduleEndAddr; @@ -132,6 +139,35 @@ public static partial class Util return gitHashClientStructsInternal; } + /// + /// Gets the Git branch name this version of Dalamud was built from, or null, if this is a Debug build. + /// + /// The branch name. + public static string? GetGitBranch() + { + if (branchInternal != null) + return branchInternal; + + var asm = typeof(Util).Assembly; + var attrs = asm.GetCustomAttributes(); + + var gitBranch = attrs.FirstOrDefault(a => a.Key == "GitBranch")?.Value; + if (gitBranch == null) + return null; + + return branchInternal = gitBranch; + } + + /// + /// Gets the active Dalamud track, if this instance was launched through XIVLauncher and used a version + /// downloaded from webservices. + /// + /// The name of the track, or null. + internal static string? GetActiveTrack() + { + return Environment.GetEnvironmentVariable("DALAMUD_BRANCH"); + } + /// public static unsafe string DescribeAddress(void* p) => DescribeAddress((nint)p); @@ -522,17 +558,36 @@ public static partial class Util public static bool IsWindows11() => Environment.OSVersion.Version.Build >= 22000; /// - /// Open a link in the default browser. + /// Open a link in the default browser, and attempts to focus the newly launched application. /// /// The link to open. - public static void OpenLink(string url) - { - var process = new ProcessStartInfo(url) + public static void OpenLink(string url) => new Thread( + static url => { - UseShellExecute = true, - }; - Process.Start(process); - } + try + { + var psi = new ProcessStartInfo((string)url!) + { + UseShellExecute = true, + ErrorDialogParentHandle = Service.GetNullable() is { } im + ? im.GameWindowHandle + : 0, + Verb = "open", + }; + if (Process.Start(psi) is not { } process) + return; + + if (process.Id != 0) + TerraFX.Interop.Windows.Windows.AllowSetForegroundWindow((uint)process.Id); + process.WaitForInputIdle(); + TerraFX.Interop.Windows.Windows.SetForegroundWindow( + (TerraFX.Interop.Windows.HWND)process.MainWindowHandle); + } + catch (Exception e) + { + Log.Error(e, "{fn}: failed to open {url}", nameof(OpenLink), url); + } + }).Start(url); /// /// Perform a "zipper merge" (A, 1, B, 2, C, 3) of multiple enumerables, allowing for lists to end early. @@ -731,7 +786,7 @@ public static partial class Util $"{actor.Address.ToInt64():X}:{actor.GameObjectId:X}[{tag}] - {actor.ObjectKind} - {actor.Name} - X{actor.Position.X} Y{actor.Position.Y} Z{actor.Position.Z} D{actor.YalmDistanceX} R{actor.Rotation} - Target: {actor.TargetObjectId:X}\n"; if (actor is Npc npc) - actorString += $" DataId: {npc.DataId} NameId:{npc.NameId}\n"; + actorString += $" BaseId: {npc.BaseId} NameId:{npc.NameId}\n"; if (actor is ICharacter chara) { @@ -765,7 +820,7 @@ public static partial class Util $"{actor.Address.ToInt64():X}:{actor.GameObjectId:X}[{tag}] - {actor.ObjectKind} - {actor.Name} - X{actor.Position.X} Y{actor.Position.Y} Z{actor.Position.Z} D{actor.YalmDistanceX} R{actor.Rotation} - Target: {actor.TargetObjectId:X}\n"; if (actor is Npc npc) - actorString += $" DataId: {npc.DataId} NameId:{npc.NameId}\n"; + actorString += $" BaseId: {npc.BaseId} NameId:{npc.NameId}\n"; if (actor is Character chara) { diff --git a/DalamudCrashHandler/DalamudCrashHandler.cpp b/DalamudCrashHandler/DalamudCrashHandler.cpp index 62ccdd20a..ec7115ffd 100644 --- a/DalamudCrashHandler/DalamudCrashHandler.cpp +++ b/DalamudCrashHandler/DalamudCrashHandler.cpp @@ -463,12 +463,14 @@ void open_folder_and_select_items(HWND hwndOpener, const std::wstring& path) { void export_tspack(HWND hWndParent, const std::filesystem::path& logDir, const std::string& crashLog, const std::string& troubleshootingPackData) { static const char* SourceLogFiles[] = { - "output.log", + "output.log", // XIVLauncher for Windows + "launcher.log", // XIVLauncher.Core for [mostly] Linux "patcher.log", "dalamud.log", "dalamud.injector.log", "dalamud.boot.log", "aria.log", + "wine.log" }; static constexpr auto MaxSizePerLog = 1 * 1024 * 1024; static constexpr std::array OutputFileTypeFilterSpec{{ diff --git a/Directory.Build.props b/Directory.Build.props index 92902b0c0..f9f061c17 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,18 +6,15 @@ x64 x64 13.0 - - - - 5.7.0 - 7.2.2 - 13.0.3 + + + false - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index bbf9ee1ce..547559d1b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Dalamud [![Actions Status](https://github.com/goatcorp/Dalamud/workflows/Build%20Dalamud/badge.svg)](https://github.com/goatcorp/Dalamud/actions) [![Discord Shield](https://discordapp.com/api/guilds/581875019861328007/widget.png?style=shield)](https://discord.gg/3NMcUV5) +# Dalamud [![Discord Shield](https://discordapp.com/api/guilds/581875019861328007/widget.png?style=shield)](https://discord.gg/3NMcUV5)

Dalamud diff --git a/build/build.csproj b/build/build.csproj index 37a4d3252..b4aaa959d 100644 --- a/build/build.csproj +++ b/build/build.csproj @@ -8,6 +8,7 @@ .. 1 true + false diff --git a/filter_imgui_bindings.ps1 b/filter_imgui_bindings.ps1 index e2ca0d7ba..55f02599d 100644 --- a/filter_imgui_bindings.ps1 +++ b/filter_imgui_bindings.ps1 @@ -21,7 +21,7 @@ $sourcePaths = ( # replace "ImGuiKey.GamepadStart" $tmp = Get-Content -Path "$PSScriptRoot\imgui\Dalamud.Bindings.ImGui\Generated\Enums\ImGuiKeyPrivate.cs" -Raw $tmp = $tmp.Replace("unchecked((int)GamepadStart)", "unchecked((int)ImGuiKey.GamepadStart)").Trim() -$tmp | Set-Content -Path "$PSScriptRoot\imgui\Dalamud.Bindings.ImGui\Generated\Enums\ImGuiKeyPrivate.cs" -Encoding ascii +$tmp.Trim() | Set-Content -Path "$PSScriptRoot\imgui\Dalamud.Bindings.ImGui\Generated\Enums\ImGuiKeyPrivate.cs" -Encoding ascii try { @@ -141,7 +141,9 @@ foreach ($sourcePath in $sourcePaths) $husks = $husks.Replace("public unsafe struct", "public unsafe partial struct") $husks = $referNativeFunctionQualified.Replace($husks, '$1Native.$2') $husks = "// `r`n`r`nusing $([string]::Join(";`r`nusing ", $imports) );`r`n`r`n$husks" - $husks | Set-Content -Path "$targetPath.gen.cs" -Encoding ascii + $husks = $husks -ireplace 'nuint (ActiveIdUsingKeyInputMask)', 'ImBitArrayImGuiKeyNamedKeyCOUNTLessImGuiKeyNamedKeyBEGIN $1' + $husks = $husks.Replace('ref Unsafe.AsRef(&Handle->ActiveIdUsingKeyInputMask)', 'ref Unsafe.AsRef(&Handle->ActiveIdUsingKeyInputMask)') + $husks.Trim() | Set-Content -Path "$targetPath.gen.cs" -Encoding ascii } $husks = "// `r`n`r`nusing $([string]::Join(";`r`nusing ", $imports) );`r`n`r`nnamespace $namespace;`r`n`r`n" @@ -286,6 +288,6 @@ foreach ($sourcePath in $sourcePaths) $null = $sb.Append("// DISCARDED: $methodName`r`n") } - $sb.ToString() | Set-Content -Path "$targetPath/$className.gen.cs" -Encoding ascii + $sb.ToString().Trim() | Set-Content -Path "$targetPath/$className.gen.cs" -Encoding ascii } } diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Functions/ImGui.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Functions/ImGui.gen.cs index 3574ab21f..8db9fb7de 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Functions/ImGui.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Functions/ImGui.gen.cs @@ -8896,4 +8896,3 @@ public unsafe partial class ImGui // DISCARDED: internal static byte VSliderFloatNative(byte* label, Vector2 size, float* v, float vMin, float vMax, byte* format, ImGuiSliderFlags flags) // DISCARDED: internal static byte VSliderIntNative(byte* label, Vector2 size, int* v, int vMin, int vMax, byte* format, ImGuiSliderFlags flags) // DISCARDED: internal static byte VSliderScalarNative(byte* label, Vector2 size, ImGuiDataType dataType, void* pData, void* pMin, void* pMax, byte* format, ImGuiSliderFlags flags) - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Functions/ImGuiNative.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Functions/ImGuiNative.gen.cs index 22386e992..5e2fd7fa2 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Functions/ImGuiNative.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Functions/ImGuiNative.gen.cs @@ -4828,4 +4828,3 @@ public unsafe partial class ImGuiNative } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs.gen.cs index f853f5402..2fe66de88 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs.gen.cs @@ -2250,7 +2250,7 @@ namespace Dalamud.Bindings.ImGui public byte ActiveIdUsingMouseWheel; public uint ActiveIdUsingNavDirMask; public uint ActiveIdUsingNavInputMask; - public nuint ActiveIdUsingKeyInputMask; + public ImBitArrayImGuiKeyNamedKeyCOUNTLessImGuiKeyNamedKeyBEGIN ActiveIdUsingKeyInputMask; public ImGuiItemFlags CurrentItemFlags; public ImGuiNextItemData NextItemData; public ImGuiLastItemData LastItemData; @@ -2546,7 +2546,7 @@ namespace Dalamud.Bindings.ImGui public int WantCaptureKeyboardNextFrame; public int WantTextInputNextFrame; public ImVector TempBuffer; - public unsafe ImGuiContext(bool initialized = default, bool fontAtlasOwnedByContext = default, ImGuiIO io = default, ImGuiPlatformIO platformIo = default, ImVector inputEventsQueue = default, ImVector inputEventsTrail = default, ImGuiStyle style = default, ImGuiConfigFlags configFlagsCurrFrame = default, ImGuiConfigFlags configFlagsLastFrame = default, ImFontPtr font = default, float fontSize = default, float fontBaseSize = default, ImDrawListSharedData drawListSharedData = default, double time = default, int frameCount = default, int frameCountEnded = default, int frameCountPlatformEnded = default, int frameCountRendered = default, bool withinFrameScope = default, bool withinFrameScopeWithImplicitWindow = default, bool withinEndChild = default, bool gcCompactAll = default, bool testEngineHookItems = default, void* testEngine = default, ImVector windows = default, ImVector windowsFocusOrder = default, ImVector windowsTempSortBuffer = default, ImVector currentWindowStack = default, ImGuiStorage windowsById = default, int windowsActiveCount = default, Vector2 windowsHoverPadding = default, ImGuiWindow* currentWindow = default, ImGuiWindow* hoveredWindow = default, ImGuiWindow* hoveredWindowUnderMovingWindow = default, ImGuiDockNode* hoveredDockNode = default, ImGuiWindow* movingWindow = default, ImGuiWindow* wheelingWindow = default, Vector2 wheelingWindowRefMousePos = default, float wheelingWindowTimer = default, uint debugHookIdInfo = default, uint hoveredId = default, uint hoveredIdPreviousFrame = default, bool hoveredIdAllowOverlap = default, bool hoveredIdUsingMouseWheel = default, bool hoveredIdPreviousFrameUsingMouseWheel = default, bool hoveredIdDisabled = default, float hoveredIdTimer = default, float hoveredIdNotActiveTimer = default, uint activeId = default, uint activeIdIsAlive = default, float activeIdTimer = default, bool activeIdIsJustActivated = default, bool activeIdAllowOverlap = default, bool activeIdNoClearOnFocusLoss = default, bool activeIdHasBeenPressedBefore = default, bool activeIdHasBeenEditedBefore = default, bool activeIdHasBeenEditedThisFrame = default, Vector2 activeIdClickOffset = default, ImGuiWindow* activeIdWindow = default, ImGuiInputSource activeIdSource = default, int activeIdMouseButton = default, uint activeIdPreviousFrame = default, bool activeIdPreviousFrameIsAlive = default, bool activeIdPreviousFrameHasBeenEditedBefore = default, ImGuiWindow* activeIdPreviousFrameWindow = default, uint lastActiveId = default, float lastActiveIdTimer = default, bool activeIdUsingMouseWheel = default, uint activeIdUsingNavDirMask = default, uint activeIdUsingNavInputMask = default, nuint activeIdUsingKeyInputMask = default, ImGuiItemFlags currentItemFlags = default, ImGuiNextItemData nextItemData = default, ImGuiLastItemData lastItemData = default, ImGuiNextWindowData nextWindowData = default, ImVector colorStack = default, ImVector styleVarStack = default, ImVector fontStack = default, ImVector focusScopeStack = default, ImVector itemFlagsStack = default, ImVector groupStack = default, ImVector openPopupStack = default, ImVector beginPopupStack = default, int beginMenuCount = default, ImVector viewports = default, float currentDpiScale = default, ImGuiViewportP* currentViewport = default, ImGuiViewportP* mouseViewport = default, ImGuiViewportP* mouseLastHoveredViewport = default, uint platformLastFocusedViewportId = default, ImGuiPlatformMonitor fallbackMonitor = default, int viewportFrontMostStampCount = default, ImGuiWindow* navWindow = default, uint navId = default, uint navFocusScopeId = default, uint navActivateId = default, uint navActivateDownId = default, uint navActivatePressedId = default, uint navActivateInputId = default, ImGuiActivateFlags navActivateFlags = default, uint navJustMovedToId = default, uint navJustMovedToFocusScopeId = default, ImGuiModFlags navJustMovedToKeyMods = default, uint navNextActivateId = default, ImGuiActivateFlags navNextActivateFlags = default, ImGuiInputSource navInputSource = default, ImGuiNavLayer navLayer = default, bool navIdIsAlive = default, bool navMousePosDirty = default, bool navDisableHighlight = default, bool navDisableMouseHover = default, bool navAnyRequest = default, bool navInitRequest = default, bool navInitRequestFromMove = default, uint navInitResultId = default, ImRect navInitResultRectRel = default, bool navMoveSubmitted = default, bool navMoveScoringItems = default, bool navMoveForwardToNextFrame = default, ImGuiNavMoveFlags navMoveFlags = default, ImGuiScrollFlags navMoveScrollFlags = default, ImGuiModFlags navMoveKeyMods = default, ImGuiDir navMoveDir = default, ImGuiDir navMoveDirForDebug = default, ImGuiDir navMoveClipDir = default, ImRect navScoringRect = default, ImRect navScoringNoClipRect = default, int navScoringDebugCount = default, int navTabbingDir = default, int navTabbingCounter = default, ImGuiNavItemData navMoveResultLocal = default, ImGuiNavItemData navMoveResultLocalVisible = default, ImGuiNavItemData navMoveResultOther = default, ImGuiNavItemData navTabbingResultFirst = default, ImGuiWindow* navWindowingTarget = default, ImGuiWindow* navWindowingTargetAnim = default, ImGuiWindow* navWindowingListWindow = default, float navWindowingTimer = default, float navWindowingHighlightAlpha = default, bool navWindowingToggleLayer = default, float dimBgRatio = default, ImGuiMouseCursor mouseCursor = default, bool dragDropActive = default, bool dragDropWithinSource = default, bool dragDropWithinTarget = default, ImGuiDragDropFlags dragDropSourceFlags = default, int dragDropSourceFrameCount = default, int dragDropMouseButton = default, ImGuiPayload dragDropPayload = default, ImRect dragDropTargetRect = default, uint dragDropTargetId = default, ImGuiDragDropFlags dragDropAcceptFlags = default, float dragDropAcceptIdCurrRectSurface = default, uint dragDropAcceptIdCurr = default, uint dragDropAcceptIdPrev = default, int dragDropAcceptFrameCount = default, uint dragDropHoldJustPressedId = default, ImVector dragDropPayloadBufHeap = default, byte* dragDropPayloadBufLocal = default, int clipperTempDataStacked = default, ImVector clipperTempData = default, ImGuiTable* currentTable = default, int tablesTempDataStacked = default, ImVector tablesTempData = default, ImPoolImGuiTable tables = default, ImVector tablesLastTimeActive = default, ImVector drawChannelsTempMergeBuffer = default, ImGuiTabBar* currentTabBar = default, ImPoolImGuiTabBar tabBars = default, ImVector currentTabBarStack = default, ImVector shrinkWidthBuffer = default, Vector2 mouseLastValidPos = default, ImGuiInputTextState inputTextState = default, ImFont inputTextPasswordFont = default, uint tempInputId = default, ImGuiColorEditFlags colorEditOptions = default, float colorEditLastHue = default, float colorEditLastSat = default, uint colorEditLastColor = default, Vector4 colorPickerRef = default, ImGuiComboPreviewData comboPreviewData = default, float sliderGrabClickOffset = default, float sliderCurrentAccum = default, bool sliderCurrentAccumDirty = default, bool dragCurrentAccumDirty = default, float dragCurrentAccum = default, float dragSpeedDefaultRatio = default, float scrollbarClickDeltaToGrabCenter = default, float disabledAlphaBackup = default, short disabledStackSize = default, short tooltipOverrideCount = default, float tooltipSlowDelay = default, ImVector clipboardHandlerData = default, ImVector menusIdSubmittedThisFrame = default, ImGuiPlatformImeData platformImeData = default, ImGuiPlatformImeData platformImeDataPrev = default, uint platformImeViewport = default, byte platformLocaleDecimalPoint = default, ImGuiDockContext dockContext = default, bool settingsLoaded = default, float settingsDirtyTimer = default, ImGuiTextBuffer settingsIniData = default, ImVector settingsHandlers = default, ImChunkStreamImGuiWindowSettings settingsWindows = default, ImChunkStreamImGuiTableSettings settingsTables = default, ImVector hooks = default, uint hookIdNext = default, bool logEnabled = default, ImGuiLogType logType = default, ImFileHandle logFile = default, ImGuiTextBuffer logBuffer = default, byte* logNextPrefix = default, byte* logNextSuffix = default, float logLinePosY = default, bool logLineFirstItem = default, int logDepthRef = default, int logDepthToExpand = default, int logDepthToExpandDefault = default, ImGuiDebugLogFlags debugLogFlags = default, ImGuiTextBuffer debugLogBuf = default, bool debugItemPickerActive = default, uint debugItemPickerBreakId = default, ImGuiMetricsConfig debugMetricsConfig = default, ImGuiStackTool debugStackTool = default, float* framerateSecPerFrame = default, int framerateSecPerFrameIdx = default, int framerateSecPerFrameCount = default, float framerateSecPerFrameAccum = default, int wantCaptureMouseNextFrame = default, int wantCaptureKeyboardNextFrame = default, int wantTextInputNextFrame = default, ImVector tempBuffer = default) + public unsafe ImGuiContext(bool initialized = default, bool fontAtlasOwnedByContext = default, ImGuiIO io = default, ImGuiPlatformIO platformIo = default, ImVector inputEventsQueue = default, ImVector inputEventsTrail = default, ImGuiStyle style = default, ImGuiConfigFlags configFlagsCurrFrame = default, ImGuiConfigFlags configFlagsLastFrame = default, ImFontPtr font = default, float fontSize = default, float fontBaseSize = default, ImDrawListSharedData drawListSharedData = default, double time = default, int frameCount = default, int frameCountEnded = default, int frameCountPlatformEnded = default, int frameCountRendered = default, bool withinFrameScope = default, bool withinFrameScopeWithImplicitWindow = default, bool withinEndChild = default, bool gcCompactAll = default, bool testEngineHookItems = default, void* testEngine = default, ImVector windows = default, ImVector windowsFocusOrder = default, ImVector windowsTempSortBuffer = default, ImVector currentWindowStack = default, ImGuiStorage windowsById = default, int windowsActiveCount = default, Vector2 windowsHoverPadding = default, ImGuiWindow* currentWindow = default, ImGuiWindow* hoveredWindow = default, ImGuiWindow* hoveredWindowUnderMovingWindow = default, ImGuiDockNode* hoveredDockNode = default, ImGuiWindow* movingWindow = default, ImGuiWindow* wheelingWindow = default, Vector2 wheelingWindowRefMousePos = default, float wheelingWindowTimer = default, uint debugHookIdInfo = default, uint hoveredId = default, uint hoveredIdPreviousFrame = default, bool hoveredIdAllowOverlap = default, bool hoveredIdUsingMouseWheel = default, bool hoveredIdPreviousFrameUsingMouseWheel = default, bool hoveredIdDisabled = default, float hoveredIdTimer = default, float hoveredIdNotActiveTimer = default, uint activeId = default, uint activeIdIsAlive = default, float activeIdTimer = default, bool activeIdIsJustActivated = default, bool activeIdAllowOverlap = default, bool activeIdNoClearOnFocusLoss = default, bool activeIdHasBeenPressedBefore = default, bool activeIdHasBeenEditedBefore = default, bool activeIdHasBeenEditedThisFrame = default, Vector2 activeIdClickOffset = default, ImGuiWindow* activeIdWindow = default, ImGuiInputSource activeIdSource = default, int activeIdMouseButton = default, uint activeIdPreviousFrame = default, bool activeIdPreviousFrameIsAlive = default, bool activeIdPreviousFrameHasBeenEditedBefore = default, ImGuiWindow* activeIdPreviousFrameWindow = default, uint lastActiveId = default, float lastActiveIdTimer = default, bool activeIdUsingMouseWheel = default, uint activeIdUsingNavDirMask = default, uint activeIdUsingNavInputMask = default, ImBitArrayImGuiKeyNamedKeyCOUNTLessImGuiKeyNamedKeyBEGIN activeIdUsingKeyInputMask = default, ImGuiItemFlags currentItemFlags = default, ImGuiNextItemData nextItemData = default, ImGuiLastItemData lastItemData = default, ImGuiNextWindowData nextWindowData = default, ImVector colorStack = default, ImVector styleVarStack = default, ImVector fontStack = default, ImVector focusScopeStack = default, ImVector itemFlagsStack = default, ImVector groupStack = default, ImVector openPopupStack = default, ImVector beginPopupStack = default, int beginMenuCount = default, ImVector viewports = default, float currentDpiScale = default, ImGuiViewportP* currentViewport = default, ImGuiViewportP* mouseViewport = default, ImGuiViewportP* mouseLastHoveredViewport = default, uint platformLastFocusedViewportId = default, ImGuiPlatformMonitor fallbackMonitor = default, int viewportFrontMostStampCount = default, ImGuiWindow* navWindow = default, uint navId = default, uint navFocusScopeId = default, uint navActivateId = default, uint navActivateDownId = default, uint navActivatePressedId = default, uint navActivateInputId = default, ImGuiActivateFlags navActivateFlags = default, uint navJustMovedToId = default, uint navJustMovedToFocusScopeId = default, ImGuiModFlags navJustMovedToKeyMods = default, uint navNextActivateId = default, ImGuiActivateFlags navNextActivateFlags = default, ImGuiInputSource navInputSource = default, ImGuiNavLayer navLayer = default, bool navIdIsAlive = default, bool navMousePosDirty = default, bool navDisableHighlight = default, bool navDisableMouseHover = default, bool navAnyRequest = default, bool navInitRequest = default, bool navInitRequestFromMove = default, uint navInitResultId = default, ImRect navInitResultRectRel = default, bool navMoveSubmitted = default, bool navMoveScoringItems = default, bool navMoveForwardToNextFrame = default, ImGuiNavMoveFlags navMoveFlags = default, ImGuiScrollFlags navMoveScrollFlags = default, ImGuiModFlags navMoveKeyMods = default, ImGuiDir navMoveDir = default, ImGuiDir navMoveDirForDebug = default, ImGuiDir navMoveClipDir = default, ImRect navScoringRect = default, ImRect navScoringNoClipRect = default, int navScoringDebugCount = default, int navTabbingDir = default, int navTabbingCounter = default, ImGuiNavItemData navMoveResultLocal = default, ImGuiNavItemData navMoveResultLocalVisible = default, ImGuiNavItemData navMoveResultOther = default, ImGuiNavItemData navTabbingResultFirst = default, ImGuiWindow* navWindowingTarget = default, ImGuiWindow* navWindowingTargetAnim = default, ImGuiWindow* navWindowingListWindow = default, float navWindowingTimer = default, float navWindowingHighlightAlpha = default, bool navWindowingToggleLayer = default, float dimBgRatio = default, ImGuiMouseCursor mouseCursor = default, bool dragDropActive = default, bool dragDropWithinSource = default, bool dragDropWithinTarget = default, ImGuiDragDropFlags dragDropSourceFlags = default, int dragDropSourceFrameCount = default, int dragDropMouseButton = default, ImGuiPayload dragDropPayload = default, ImRect dragDropTargetRect = default, uint dragDropTargetId = default, ImGuiDragDropFlags dragDropAcceptFlags = default, float dragDropAcceptIdCurrRectSurface = default, uint dragDropAcceptIdCurr = default, uint dragDropAcceptIdPrev = default, int dragDropAcceptFrameCount = default, uint dragDropHoldJustPressedId = default, ImVector dragDropPayloadBufHeap = default, byte* dragDropPayloadBufLocal = default, int clipperTempDataStacked = default, ImVector clipperTempData = default, ImGuiTable* currentTable = default, int tablesTempDataStacked = default, ImVector tablesTempData = default, ImPoolImGuiTable tables = default, ImVector tablesLastTimeActive = default, ImVector drawChannelsTempMergeBuffer = default, ImGuiTabBar* currentTabBar = default, ImPoolImGuiTabBar tabBars = default, ImVector currentTabBarStack = default, ImVector shrinkWidthBuffer = default, Vector2 mouseLastValidPos = default, ImGuiInputTextState inputTextState = default, ImFont inputTextPasswordFont = default, uint tempInputId = default, ImGuiColorEditFlags colorEditOptions = default, float colorEditLastHue = default, float colorEditLastSat = default, uint colorEditLastColor = default, Vector4 colorPickerRef = default, ImGuiComboPreviewData comboPreviewData = default, float sliderGrabClickOffset = default, float sliderCurrentAccum = default, bool sliderCurrentAccumDirty = default, bool dragCurrentAccumDirty = default, float dragCurrentAccum = default, float dragSpeedDefaultRatio = default, float scrollbarClickDeltaToGrabCenter = default, float disabledAlphaBackup = default, short disabledStackSize = default, short tooltipOverrideCount = default, float tooltipSlowDelay = default, ImVector clipboardHandlerData = default, ImVector menusIdSubmittedThisFrame = default, ImGuiPlatformImeData platformImeData = default, ImGuiPlatformImeData platformImeDataPrev = default, uint platformImeViewport = default, byte platformLocaleDecimalPoint = default, ImGuiDockContext dockContext = default, bool settingsLoaded = default, float settingsDirtyTimer = default, ImGuiTextBuffer settingsIniData = default, ImVector settingsHandlers = default, ImChunkStreamImGuiWindowSettings settingsWindows = default, ImChunkStreamImGuiTableSettings settingsTables = default, ImVector hooks = default, uint hookIdNext = default, bool logEnabled = default, ImGuiLogType logType = default, ImFileHandle logFile = default, ImGuiTextBuffer logBuffer = default, byte* logNextPrefix = default, byte* logNextSuffix = default, float logLinePosY = default, bool logLineFirstItem = default, int logDepthRef = default, int logDepthToExpand = default, int logDepthToExpandDefault = default, ImGuiDebugLogFlags debugLogFlags = default, ImGuiTextBuffer debugLogBuf = default, bool debugItemPickerActive = default, uint debugItemPickerBreakId = default, ImGuiMetricsConfig debugMetricsConfig = default, ImGuiStackTool debugStackTool = default, float* framerateSecPerFrame = default, int framerateSecPerFrameIdx = default, int framerateSecPerFrameCount = default, float framerateSecPerFrameAccum = default, int wantCaptureMouseNextFrame = default, int wantCaptureKeyboardNextFrame = default, int wantTextInputNextFrame = default, ImVector tempBuffer = default) { Initialized = initialized ? (byte)1 : (byte)0; FontAtlasOwnedByContext = fontAtlasOwnedByContext ? (byte)1 : (byte)0; @@ -2921,7 +2921,7 @@ namespace Dalamud.Bindings.ImGui WantTextInputNextFrame = wantTextInputNextFrame; TempBuffer = tempBuffer; } - public unsafe ImGuiContext(bool initialized = default, bool fontAtlasOwnedByContext = default, ImGuiIO io = default, ImGuiPlatformIO platformIo = default, ImVector inputEventsQueue = default, ImVector inputEventsTrail = default, ImGuiStyle style = default, ImGuiConfigFlags configFlagsCurrFrame = default, ImGuiConfigFlags configFlagsLastFrame = default, ImFontPtr font = default, float fontSize = default, float fontBaseSize = default, ImDrawListSharedData drawListSharedData = default, double time = default, int frameCount = default, int frameCountEnded = default, int frameCountPlatformEnded = default, int frameCountRendered = default, bool withinFrameScope = default, bool withinFrameScopeWithImplicitWindow = default, bool withinEndChild = default, bool gcCompactAll = default, bool testEngineHookItems = default, void* testEngine = default, ImVector windows = default, ImVector windowsFocusOrder = default, ImVector windowsTempSortBuffer = default, ImVector currentWindowStack = default, ImGuiStorage windowsById = default, int windowsActiveCount = default, Vector2 windowsHoverPadding = default, ImGuiWindow* currentWindow = default, ImGuiWindow* hoveredWindow = default, ImGuiWindow* hoveredWindowUnderMovingWindow = default, ImGuiDockNode* hoveredDockNode = default, ImGuiWindow* movingWindow = default, ImGuiWindow* wheelingWindow = default, Vector2 wheelingWindowRefMousePos = default, float wheelingWindowTimer = default, uint debugHookIdInfo = default, uint hoveredId = default, uint hoveredIdPreviousFrame = default, bool hoveredIdAllowOverlap = default, bool hoveredIdUsingMouseWheel = default, bool hoveredIdPreviousFrameUsingMouseWheel = default, bool hoveredIdDisabled = default, float hoveredIdTimer = default, float hoveredIdNotActiveTimer = default, uint activeId = default, uint activeIdIsAlive = default, float activeIdTimer = default, bool activeIdIsJustActivated = default, bool activeIdAllowOverlap = default, bool activeIdNoClearOnFocusLoss = default, bool activeIdHasBeenPressedBefore = default, bool activeIdHasBeenEditedBefore = default, bool activeIdHasBeenEditedThisFrame = default, Vector2 activeIdClickOffset = default, ImGuiWindow* activeIdWindow = default, ImGuiInputSource activeIdSource = default, int activeIdMouseButton = default, uint activeIdPreviousFrame = default, bool activeIdPreviousFrameIsAlive = default, bool activeIdPreviousFrameHasBeenEditedBefore = default, ImGuiWindow* activeIdPreviousFrameWindow = default, uint lastActiveId = default, float lastActiveIdTimer = default, bool activeIdUsingMouseWheel = default, uint activeIdUsingNavDirMask = default, uint activeIdUsingNavInputMask = default, nuint activeIdUsingKeyInputMask = default, ImGuiItemFlags currentItemFlags = default, ImGuiNextItemData nextItemData = default, ImGuiLastItemData lastItemData = default, ImGuiNextWindowData nextWindowData = default, ImVector colorStack = default, ImVector styleVarStack = default, ImVector fontStack = default, ImVector focusScopeStack = default, ImVector itemFlagsStack = default, ImVector groupStack = default, ImVector openPopupStack = default, ImVector beginPopupStack = default, int beginMenuCount = default, ImVector viewports = default, float currentDpiScale = default, ImGuiViewportP* currentViewport = default, ImGuiViewportP* mouseViewport = default, ImGuiViewportP* mouseLastHoveredViewport = default, uint platformLastFocusedViewportId = default, ImGuiPlatformMonitor fallbackMonitor = default, int viewportFrontMostStampCount = default, ImGuiWindow* navWindow = default, uint navId = default, uint navFocusScopeId = default, uint navActivateId = default, uint navActivateDownId = default, uint navActivatePressedId = default, uint navActivateInputId = default, ImGuiActivateFlags navActivateFlags = default, uint navJustMovedToId = default, uint navJustMovedToFocusScopeId = default, ImGuiModFlags navJustMovedToKeyMods = default, uint navNextActivateId = default, ImGuiActivateFlags navNextActivateFlags = default, ImGuiInputSource navInputSource = default, ImGuiNavLayer navLayer = default, bool navIdIsAlive = default, bool navMousePosDirty = default, bool navDisableHighlight = default, bool navDisableMouseHover = default, bool navAnyRequest = default, bool navInitRequest = default, bool navInitRequestFromMove = default, uint navInitResultId = default, ImRect navInitResultRectRel = default, bool navMoveSubmitted = default, bool navMoveScoringItems = default, bool navMoveForwardToNextFrame = default, ImGuiNavMoveFlags navMoveFlags = default, ImGuiScrollFlags navMoveScrollFlags = default, ImGuiModFlags navMoveKeyMods = default, ImGuiDir navMoveDir = default, ImGuiDir navMoveDirForDebug = default, ImGuiDir navMoveClipDir = default, ImRect navScoringRect = default, ImRect navScoringNoClipRect = default, int navScoringDebugCount = default, int navTabbingDir = default, int navTabbingCounter = default, ImGuiNavItemData navMoveResultLocal = default, ImGuiNavItemData navMoveResultLocalVisible = default, ImGuiNavItemData navMoveResultOther = default, ImGuiNavItemData navTabbingResultFirst = default, ImGuiWindow* navWindowingTarget = default, ImGuiWindow* navWindowingTargetAnim = default, ImGuiWindow* navWindowingListWindow = default, float navWindowingTimer = default, float navWindowingHighlightAlpha = default, bool navWindowingToggleLayer = default, float dimBgRatio = default, ImGuiMouseCursor mouseCursor = default, bool dragDropActive = default, bool dragDropWithinSource = default, bool dragDropWithinTarget = default, ImGuiDragDropFlags dragDropSourceFlags = default, int dragDropSourceFrameCount = default, int dragDropMouseButton = default, ImGuiPayload dragDropPayload = default, ImRect dragDropTargetRect = default, uint dragDropTargetId = default, ImGuiDragDropFlags dragDropAcceptFlags = default, float dragDropAcceptIdCurrRectSurface = default, uint dragDropAcceptIdCurr = default, uint dragDropAcceptIdPrev = default, int dragDropAcceptFrameCount = default, uint dragDropHoldJustPressedId = default, ImVector dragDropPayloadBufHeap = default, Span dragDropPayloadBufLocal = default, int clipperTempDataStacked = default, ImVector clipperTempData = default, ImGuiTable* currentTable = default, int tablesTempDataStacked = default, ImVector tablesTempData = default, ImPoolImGuiTable tables = default, ImVector tablesLastTimeActive = default, ImVector drawChannelsTempMergeBuffer = default, ImGuiTabBar* currentTabBar = default, ImPoolImGuiTabBar tabBars = default, ImVector currentTabBarStack = default, ImVector shrinkWidthBuffer = default, Vector2 mouseLastValidPos = default, ImGuiInputTextState inputTextState = default, ImFont inputTextPasswordFont = default, uint tempInputId = default, ImGuiColorEditFlags colorEditOptions = default, float colorEditLastHue = default, float colorEditLastSat = default, uint colorEditLastColor = default, Vector4 colorPickerRef = default, ImGuiComboPreviewData comboPreviewData = default, float sliderGrabClickOffset = default, float sliderCurrentAccum = default, bool sliderCurrentAccumDirty = default, bool dragCurrentAccumDirty = default, float dragCurrentAccum = default, float dragSpeedDefaultRatio = default, float scrollbarClickDeltaToGrabCenter = default, float disabledAlphaBackup = default, short disabledStackSize = default, short tooltipOverrideCount = default, float tooltipSlowDelay = default, ImVector clipboardHandlerData = default, ImVector menusIdSubmittedThisFrame = default, ImGuiPlatformImeData platformImeData = default, ImGuiPlatformImeData platformImeDataPrev = default, uint platformImeViewport = default, byte platformLocaleDecimalPoint = default, ImGuiDockContext dockContext = default, bool settingsLoaded = default, float settingsDirtyTimer = default, ImGuiTextBuffer settingsIniData = default, ImVector settingsHandlers = default, ImChunkStreamImGuiWindowSettings settingsWindows = default, ImChunkStreamImGuiTableSettings settingsTables = default, ImVector hooks = default, uint hookIdNext = default, bool logEnabled = default, ImGuiLogType logType = default, ImFileHandle logFile = default, ImGuiTextBuffer logBuffer = default, byte* logNextPrefix = default, byte* logNextSuffix = default, float logLinePosY = default, bool logLineFirstItem = default, int logDepthRef = default, int logDepthToExpand = default, int logDepthToExpandDefault = default, ImGuiDebugLogFlags debugLogFlags = default, ImGuiTextBuffer debugLogBuf = default, bool debugItemPickerActive = default, uint debugItemPickerBreakId = default, ImGuiMetricsConfig debugMetricsConfig = default, ImGuiStackTool debugStackTool = default, Span framerateSecPerFrame = default, int framerateSecPerFrameIdx = default, int framerateSecPerFrameCount = default, float framerateSecPerFrameAccum = default, int wantCaptureMouseNextFrame = default, int wantCaptureKeyboardNextFrame = default, int wantTextInputNextFrame = default, ImVector tempBuffer = default) + public unsafe ImGuiContext(bool initialized = default, bool fontAtlasOwnedByContext = default, ImGuiIO io = default, ImGuiPlatformIO platformIo = default, ImVector inputEventsQueue = default, ImVector inputEventsTrail = default, ImGuiStyle style = default, ImGuiConfigFlags configFlagsCurrFrame = default, ImGuiConfigFlags configFlagsLastFrame = default, ImFontPtr font = default, float fontSize = default, float fontBaseSize = default, ImDrawListSharedData drawListSharedData = default, double time = default, int frameCount = default, int frameCountEnded = default, int frameCountPlatformEnded = default, int frameCountRendered = default, bool withinFrameScope = default, bool withinFrameScopeWithImplicitWindow = default, bool withinEndChild = default, bool gcCompactAll = default, bool testEngineHookItems = default, void* testEngine = default, ImVector windows = default, ImVector windowsFocusOrder = default, ImVector windowsTempSortBuffer = default, ImVector currentWindowStack = default, ImGuiStorage windowsById = default, int windowsActiveCount = default, Vector2 windowsHoverPadding = default, ImGuiWindow* currentWindow = default, ImGuiWindow* hoveredWindow = default, ImGuiWindow* hoveredWindowUnderMovingWindow = default, ImGuiDockNode* hoveredDockNode = default, ImGuiWindow* movingWindow = default, ImGuiWindow* wheelingWindow = default, Vector2 wheelingWindowRefMousePos = default, float wheelingWindowTimer = default, uint debugHookIdInfo = default, uint hoveredId = default, uint hoveredIdPreviousFrame = default, bool hoveredIdAllowOverlap = default, bool hoveredIdUsingMouseWheel = default, bool hoveredIdPreviousFrameUsingMouseWheel = default, bool hoveredIdDisabled = default, float hoveredIdTimer = default, float hoveredIdNotActiveTimer = default, uint activeId = default, uint activeIdIsAlive = default, float activeIdTimer = default, bool activeIdIsJustActivated = default, bool activeIdAllowOverlap = default, bool activeIdNoClearOnFocusLoss = default, bool activeIdHasBeenPressedBefore = default, bool activeIdHasBeenEditedBefore = default, bool activeIdHasBeenEditedThisFrame = default, Vector2 activeIdClickOffset = default, ImGuiWindow* activeIdWindow = default, ImGuiInputSource activeIdSource = default, int activeIdMouseButton = default, uint activeIdPreviousFrame = default, bool activeIdPreviousFrameIsAlive = default, bool activeIdPreviousFrameHasBeenEditedBefore = default, ImGuiWindow* activeIdPreviousFrameWindow = default, uint lastActiveId = default, float lastActiveIdTimer = default, bool activeIdUsingMouseWheel = default, uint activeIdUsingNavDirMask = default, uint activeIdUsingNavInputMask = default, ImBitArrayImGuiKeyNamedKeyCOUNTLessImGuiKeyNamedKeyBEGIN activeIdUsingKeyInputMask = default, ImGuiItemFlags currentItemFlags = default, ImGuiNextItemData nextItemData = default, ImGuiLastItemData lastItemData = default, ImGuiNextWindowData nextWindowData = default, ImVector colorStack = default, ImVector styleVarStack = default, ImVector fontStack = default, ImVector focusScopeStack = default, ImVector itemFlagsStack = default, ImVector groupStack = default, ImVector openPopupStack = default, ImVector beginPopupStack = default, int beginMenuCount = default, ImVector viewports = default, float currentDpiScale = default, ImGuiViewportP* currentViewport = default, ImGuiViewportP* mouseViewport = default, ImGuiViewportP* mouseLastHoveredViewport = default, uint platformLastFocusedViewportId = default, ImGuiPlatformMonitor fallbackMonitor = default, int viewportFrontMostStampCount = default, ImGuiWindow* navWindow = default, uint navId = default, uint navFocusScopeId = default, uint navActivateId = default, uint navActivateDownId = default, uint navActivatePressedId = default, uint navActivateInputId = default, ImGuiActivateFlags navActivateFlags = default, uint navJustMovedToId = default, uint navJustMovedToFocusScopeId = default, ImGuiModFlags navJustMovedToKeyMods = default, uint navNextActivateId = default, ImGuiActivateFlags navNextActivateFlags = default, ImGuiInputSource navInputSource = default, ImGuiNavLayer navLayer = default, bool navIdIsAlive = default, bool navMousePosDirty = default, bool navDisableHighlight = default, bool navDisableMouseHover = default, bool navAnyRequest = default, bool navInitRequest = default, bool navInitRequestFromMove = default, uint navInitResultId = default, ImRect navInitResultRectRel = default, bool navMoveSubmitted = default, bool navMoveScoringItems = default, bool navMoveForwardToNextFrame = default, ImGuiNavMoveFlags navMoveFlags = default, ImGuiScrollFlags navMoveScrollFlags = default, ImGuiModFlags navMoveKeyMods = default, ImGuiDir navMoveDir = default, ImGuiDir navMoveDirForDebug = default, ImGuiDir navMoveClipDir = default, ImRect navScoringRect = default, ImRect navScoringNoClipRect = default, int navScoringDebugCount = default, int navTabbingDir = default, int navTabbingCounter = default, ImGuiNavItemData navMoveResultLocal = default, ImGuiNavItemData navMoveResultLocalVisible = default, ImGuiNavItemData navMoveResultOther = default, ImGuiNavItemData navTabbingResultFirst = default, ImGuiWindow* navWindowingTarget = default, ImGuiWindow* navWindowingTargetAnim = default, ImGuiWindow* navWindowingListWindow = default, float navWindowingTimer = default, float navWindowingHighlightAlpha = default, bool navWindowingToggleLayer = default, float dimBgRatio = default, ImGuiMouseCursor mouseCursor = default, bool dragDropActive = default, bool dragDropWithinSource = default, bool dragDropWithinTarget = default, ImGuiDragDropFlags dragDropSourceFlags = default, int dragDropSourceFrameCount = default, int dragDropMouseButton = default, ImGuiPayload dragDropPayload = default, ImRect dragDropTargetRect = default, uint dragDropTargetId = default, ImGuiDragDropFlags dragDropAcceptFlags = default, float dragDropAcceptIdCurrRectSurface = default, uint dragDropAcceptIdCurr = default, uint dragDropAcceptIdPrev = default, int dragDropAcceptFrameCount = default, uint dragDropHoldJustPressedId = default, ImVector dragDropPayloadBufHeap = default, Span dragDropPayloadBufLocal = default, int clipperTempDataStacked = default, ImVector clipperTempData = default, ImGuiTable* currentTable = default, int tablesTempDataStacked = default, ImVector tablesTempData = default, ImPoolImGuiTable tables = default, ImVector tablesLastTimeActive = default, ImVector drawChannelsTempMergeBuffer = default, ImGuiTabBar* currentTabBar = default, ImPoolImGuiTabBar tabBars = default, ImVector currentTabBarStack = default, ImVector shrinkWidthBuffer = default, Vector2 mouseLastValidPos = default, ImGuiInputTextState inputTextState = default, ImFont inputTextPasswordFont = default, uint tempInputId = default, ImGuiColorEditFlags colorEditOptions = default, float colorEditLastHue = default, float colorEditLastSat = default, uint colorEditLastColor = default, Vector4 colorPickerRef = default, ImGuiComboPreviewData comboPreviewData = default, float sliderGrabClickOffset = default, float sliderCurrentAccum = default, bool sliderCurrentAccumDirty = default, bool dragCurrentAccumDirty = default, float dragCurrentAccum = default, float dragSpeedDefaultRatio = default, float scrollbarClickDeltaToGrabCenter = default, float disabledAlphaBackup = default, short disabledStackSize = default, short tooltipOverrideCount = default, float tooltipSlowDelay = default, ImVector clipboardHandlerData = default, ImVector menusIdSubmittedThisFrame = default, ImGuiPlatformImeData platformImeData = default, ImGuiPlatformImeData platformImeDataPrev = default, uint platformImeViewport = default, byte platformLocaleDecimalPoint = default, ImGuiDockContext dockContext = default, bool settingsLoaded = default, float settingsDirtyTimer = default, ImGuiTextBuffer settingsIniData = default, ImVector settingsHandlers = default, ImChunkStreamImGuiWindowSettings settingsWindows = default, ImChunkStreamImGuiTableSettings settingsTables = default, ImVector hooks = default, uint hookIdNext = default, bool logEnabled = default, ImGuiLogType logType = default, ImFileHandle logFile = default, ImGuiTextBuffer logBuffer = default, byte* logNextPrefix = default, byte* logNextSuffix = default, float logLinePosY = default, bool logLineFirstItem = default, int logDepthRef = default, int logDepthToExpand = default, int logDepthToExpandDefault = default, ImGuiDebugLogFlags debugLogFlags = default, ImGuiTextBuffer debugLogBuf = default, bool debugItemPickerActive = default, uint debugItemPickerBreakId = default, ImGuiMetricsConfig debugMetricsConfig = default, ImGuiStackTool debugStackTool = default, Span framerateSecPerFrame = default, int framerateSecPerFrameIdx = default, int framerateSecPerFrameCount = default, float framerateSecPerFrameAccum = default, int wantCaptureMouseNextFrame = default, int wantCaptureKeyboardNextFrame = default, int wantTextInputNextFrame = default, ImVector tempBuffer = default) { Initialized = initialized ? (byte)1 : (byte)0; FontAtlasOwnedByContext = fontAtlasOwnedByContext ? (byte)1 : (byte)0; @@ -3388,7 +3388,7 @@ namespace Dalamud.Bindings.ImGui public ref bool ActiveIdUsingMouseWheel => ref Unsafe.AsRef(&Handle->ActiveIdUsingMouseWheel); public ref uint ActiveIdUsingNavDirMask => ref Unsafe.AsRef(&Handle->ActiveIdUsingNavDirMask); public ref uint ActiveIdUsingNavInputMask => ref Unsafe.AsRef(&Handle->ActiveIdUsingNavInputMask); - public ref nuint ActiveIdUsingKeyInputMask => ref Unsafe.AsRef(&Handle->ActiveIdUsingKeyInputMask); + public ref ImBitArrayImGuiKeyNamedKeyCOUNTLessImGuiKeyNamedKeyBEGIN ActiveIdUsingKeyInputMask => ref Unsafe.AsRef(&Handle->ActiveIdUsingKeyInputMask); public ref ImGuiItemFlags CurrentItemFlags => ref Unsafe.AsRef(&Handle->CurrentItemFlags); public ref ImGuiNextItemData NextItemData => ref Unsafe.AsRef(&Handle->NextItemData); public ref ImGuiLastItemData LastItemData => ref Unsafe.AsRef(&Handle->LastItemData); diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImBitArrayImGuiKeyNamedKeyCOUNTLessImGuiKeyNamedKeyBEGIN.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImBitArrayImGuiKeyNamedKeyCOUNTLessImGuiKeyNamedKeyBEGIN.gen.cs index 90874f319..7ff9da775 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImBitArrayImGuiKeyNamedKeyCOUNTLessImGuiKeyNamedKeyBEGIN.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImBitArrayImGuiKeyNamedKeyCOUNTLessImGuiKeyNamedKeyBEGIN.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImBitArrayImGuiKeyNamedKeyCOUNTLessImGuiKeyNamedKeyBEGIN { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImBitVector.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImBitVector.gen.cs index 683dc30c5..e685b531e 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImBitVector.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImBitVector.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImBitVector { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImBitVectorPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImBitVectorPtr.gen.cs index 377e9d268..48a258a96 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImBitVectorPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImBitVectorPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImBitVectorPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImChunkStreamImGuiTableSettings.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImChunkStreamImGuiTableSettings.gen.cs index 4873416b0..1ee1487f2 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImChunkStreamImGuiTableSettings.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImChunkStreamImGuiTableSettings.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImChunkStreamImGuiTableSettings { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImChunkStreamImGuiWindowSettings.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImChunkStreamImGuiWindowSettings.gen.cs index 383d7d72e..7120eb72f 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImChunkStreamImGuiWindowSettings.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImChunkStreamImGuiWindowSettings.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImChunkStreamImGuiWindowSettings { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImColor.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImColor.gen.cs index 976499f32..1165e1fa5 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImColor.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImColor.gen.cs @@ -47,4 +47,3 @@ public unsafe partial struct ImColor } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImColorPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImColorPtr.gen.cs index 80de55e64..65a07e1b2 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImColorPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImColorPtr.gen.cs @@ -32,4 +32,3 @@ public unsafe partial struct ImColorPtr ImGuiNative.SetHSV(Handle, h, s, v, (float)(1.0f)); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawChannel.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawChannel.gen.cs index afcb1db3b..a188a6c30 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawChannel.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawChannel.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImDrawChannel { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawChannelPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawChannelPtr.gen.cs index ffff450fd..950f370f1 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawChannelPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawChannelPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImDrawChannelPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawCmd.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawCmd.gen.cs index 5128fa69e..1d62c7fb4 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawCmd.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawCmd.gen.cs @@ -27,4 +27,3 @@ public unsafe partial struct ImDrawCmd } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawCmdHeader.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawCmdHeader.gen.cs index 8d0c05d8b..cb06d8201 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawCmdHeader.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawCmdHeader.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImDrawCmdHeader { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawCmdPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawCmdPtr.gen.cs index 2041f8013..a09491ded 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawCmdPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawCmdPtr.gen.cs @@ -21,4 +21,3 @@ public unsafe partial struct ImDrawCmdPtr return ret; } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawData.gen.cs index 8bdbab18d..d4c0a3c5f 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawData.gen.cs @@ -40,4 +40,3 @@ public unsafe partial struct ImDrawData } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawDataBuilder.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawDataBuilder.gen.cs index d67f3397a..756d111ba 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawDataBuilder.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawDataBuilder.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImDrawDataBuilder { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawDataBuilderPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawDataBuilderPtr.gen.cs index 89a4e3854..d513628ff 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawDataBuilderPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawDataBuilderPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImDrawDataBuilderPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawDataPtr.gen.cs index 99e7cd6e5..50b13808d 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawDataPtr.gen.cs @@ -28,4 +28,3 @@ public unsafe partial struct ImDrawDataPtr ImGuiNative.ScaleClipRects(Handle, fbScale); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawList.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawList.gen.cs index e8bcc92da..83fe8f18e 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawList.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawList.gen.cs @@ -756,4 +756,3 @@ public unsafe partial struct ImDrawList } } // DISCARDED: AddText - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListPtr.gen.cs index c223bc08b..ef82004a4 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListPtr.gen.cs @@ -441,4 +441,3 @@ public unsafe partial struct ImDrawListPtr } } // DISCARDED: AddText - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListPtrPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListPtrPtr.gen.cs index 8c9554aee..dc2d7dca1 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListPtrPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListPtrPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImDrawListPtrPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSharedData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSharedData.gen.cs index 8597857d6..d51b5fdc8 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSharedData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSharedData.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImDrawListSharedData } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSharedDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSharedDataPtr.gen.cs index 8fcb7fb71..2a4a3fd06 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSharedDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSharedDataPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImDrawListSharedDataPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSplitter.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSplitter.gen.cs index 4d34de6a1..569ccf345 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSplitter.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSplitter.gen.cs @@ -84,4 +84,3 @@ public unsafe partial struct ImDrawListSplitter } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSplitterPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSplitterPtr.gen.cs index f2fc439cf..60be9629f 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSplitterPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawListSplitterPtr.gen.cs @@ -57,4 +57,3 @@ public unsafe partial struct ImDrawListSplitterPtr } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawVert.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawVert.gen.cs index c5ef76d3e..e2ae605b5 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawVert.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawVert.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImDrawVert { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawVertPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawVertPtr.gen.cs index 58993c324..909401d5d 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawVertPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImDrawVertPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImDrawVertPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFont.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFont.gen.cs index 8c55dc024..95a60e10b 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFont.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFont.gen.cs @@ -174,4 +174,3 @@ public unsafe partial struct ImFont // DISCARDED: GetDebugName // DISCARDED: GetDebugNameS // DISCARDED: RenderText - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlas.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlas.gen.cs index 1a12ee615..bdee51eee 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlas.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlas.gen.cs @@ -1829,4 +1829,3 @@ public unsafe partial struct ImFontAtlas // DISCARDED: AddFontFromMemoryCompressedBase85TTF // DISCARDED: AddFontFromMemoryCompressedTTF // DISCARDED: AddFontFromMemoryTTF - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasCustomRect.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasCustomRect.gen.cs index 8d1a7dcbc..304b1c152 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasCustomRect.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasCustomRect.gen.cs @@ -27,4 +27,3 @@ public unsafe partial struct ImFontAtlasCustomRect } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasCustomRectPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasCustomRectPtr.gen.cs index d97bc58f5..2e4beafa0 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasCustomRectPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasCustomRectPtr.gen.cs @@ -21,4 +21,3 @@ public unsafe partial struct ImFontAtlasCustomRectPtr return ret != 0; } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasPtr.gen.cs index 59693e908..1ed50cbd9 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasPtr.gen.cs @@ -1409,4 +1409,3 @@ public unsafe partial struct ImFontAtlasPtr // DISCARDED: AddFontFromMemoryCompressedBase85TTF // DISCARDED: AddFontFromMemoryCompressedTTF // DISCARDED: AddFontFromMemoryTTF - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasTexture.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasTexture.gen.cs index f1a9c60b9..865f5d198 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasTexture.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasTexture.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImFontAtlasTexture { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasTexturePtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasTexturePtr.gen.cs index c5c47f397..e20e544dd 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasTexturePtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontAtlasTexturePtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImFontAtlasTexturePtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontBuilderIO.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontBuilderIO.gen.cs index 0c3c861a3..b60374483 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontBuilderIO.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontBuilderIO.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImFontBuilderIO { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontBuilderIOPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontBuilderIOPtr.gen.cs index b61f898d3..2689d387c 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontBuilderIOPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontBuilderIOPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImFontBuilderIOPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontConfig.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontConfig.gen.cs index 1fb980e53..00fa869a6 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontConfig.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontConfig.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImFontConfig } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontConfigPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontConfigPtr.gen.cs index 876572b6f..f94d55372 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontConfigPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontConfigPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImFontConfigPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyph.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyph.gen.cs index ff4cea6bc..ea4aa3a3f 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyph.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyph.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImFontGlyph { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphHotData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphHotData.gen.cs index e53137687..394e59ca4 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphHotData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphHotData.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImFontGlyphHotData { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphHotDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphHotDataPtr.gen.cs index 953f577dc..6cec98602 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphHotDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphHotDataPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImFontGlyphHotDataPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphPtr.gen.cs index 41675a226..34154ae42 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImFontGlyphPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphRangesBuilder.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphRangesBuilder.gen.cs index a3c2002ac..384373475 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphRangesBuilder.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphRangesBuilder.gen.cs @@ -73,4 +73,3 @@ public unsafe partial struct ImFontGlyphRangesBuilder } } // DISCARDED: AddText - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphRangesBuilderPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphRangesBuilderPtr.gen.cs index 3f8022784..ddc37425b 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphRangesBuilderPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontGlyphRangesBuilderPtr.gen.cs @@ -49,4 +49,3 @@ public unsafe partial struct ImFontGlyphRangesBuilderPtr } } // DISCARDED: AddText - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontKerningPair.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontKerningPair.gen.cs index 73de0e645..414af9aaa 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontKerningPair.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontKerningPair.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImFontKerningPair { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontKerningPairPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontKerningPairPtr.gen.cs index 2dffccc80..3869c931a 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontKerningPairPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontKerningPairPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImFontKerningPairPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontPtr.gen.cs index 9d542a898..b5d92f0d9 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontPtr.gen.cs @@ -114,4 +114,3 @@ public unsafe partial struct ImFontPtr // DISCARDED: GetDebugName // DISCARDED: GetDebugNameS // DISCARDED: RenderText - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontPtrPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontPtrPtr.gen.cs index 0d9157625..a66bfcc49 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontPtrPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImFontPtrPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImFontPtrPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiColorMod.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiColorMod.gen.cs index c3d04868b..3dd326fb5 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiColorMod.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiColorMod.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiColorMod { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiColorModPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiColorModPtr.gen.cs index 0cc2105e8..53f953e56 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiColorModPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiColorModPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiColorModPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiComboPreviewData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiComboPreviewData.gen.cs index 7d9cded64..b4ef07eef 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiComboPreviewData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiComboPreviewData.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiComboPreviewData } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiComboPreviewDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiComboPreviewDataPtr.gen.cs index b45eab464..4fee6be09 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiComboPreviewDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiComboPreviewDataPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiComboPreviewDataPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContext.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContext.gen.cs index 6a05d5ec0..65b415f74 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContext.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContext.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiContext } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContextHook.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContextHook.gen.cs index a79d07809..0b908eab4 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContextHook.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContextHook.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiContextHook } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContextHookPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContextHookPtr.gen.cs index eba827aa0..763d40454 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContextHookPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContextHookPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiContextHookPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContextPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContextPtr.gen.cs index 38a28dc68..1893638a8 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContextPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiContextPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiContextPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDataTypeInfo.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDataTypeInfo.gen.cs index a5d205c3f..36274a032 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDataTypeInfo.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDataTypeInfo.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiDataTypeInfo { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDataTypeInfoPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDataTypeInfoPtr.gen.cs index 3eb3ddcfb..17c27f6cb 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDataTypeInfoPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDataTypeInfoPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiDataTypeInfoPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDataTypeTempStorage.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDataTypeTempStorage.gen.cs index c336dd27d..9e377b29e 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDataTypeTempStorage.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDataTypeTempStorage.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiDataTypeTempStorage { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockContext.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockContext.gen.cs index 119bbefe7..33947970b 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockContext.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockContext.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiDockContext } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockContextPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockContextPtr.gen.cs index 24a48b92c..1d89e3d2c 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockContextPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockContextPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiDockContextPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNode.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNode.gen.cs index 04df4a137..225cbc3d6 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNode.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNode.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiDockNode { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNodePtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNodePtr.gen.cs index 911f24ea5..da73730b7 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNodePtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNodePtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiDockNodePtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNodeSettings.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNodeSettings.gen.cs index 5ca7f3e60..e4963e63e 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNodeSettings.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNodeSettings.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiDockNodeSettings { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNodeSettingsPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNodeSettingsPtr.gen.cs index 96ca0ee7b..5334d4e01 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNodeSettingsPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockNodeSettingsPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiDockNodeSettingsPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockRequest.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockRequest.gen.cs index 096e78834..de6349e85 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockRequest.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockRequest.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiDockRequest { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockRequestPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockRequestPtr.gen.cs index 5bc7f97d3..9c3fc5498 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockRequestPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiDockRequestPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiDockRequestPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiGroupData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiGroupData.gen.cs index 1a560f690..41f9becc0 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiGroupData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiGroupData.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiGroupData { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiGroupDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiGroupDataPtr.gen.cs index b0dfc1a2f..ddd0ff2c0 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiGroupDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiGroupDataPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiGroupDataPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiIO.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiIO.gen.cs index a3550bc65..41f0bc505 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiIO.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiIO.gen.cs @@ -106,4 +106,3 @@ public unsafe partial struct ImGuiIO // DISCARDED: AddInputCharacter // DISCARDED: AddInputCharactersUTF8 // DISCARDED: AddInputCharacterUTF16 - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiIOPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiIOPtr.gen.cs index 2507869fe..e6368ff45 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiIOPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiIOPtr.gen.cs @@ -67,4 +67,3 @@ public unsafe partial struct ImGuiIOPtr // DISCARDED: AddInputCharacter // DISCARDED: AddInputCharactersUTF8 // DISCARDED: AddInputCharacterUTF16 - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEvent.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEvent.gen.cs index 3bbc1ad3c..f92fb892d 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEvent.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEvent.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiInputEvent } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventAppFocused.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventAppFocused.gen.cs index b1c379d0a..1a6617da9 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventAppFocused.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventAppFocused.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiInputEventAppFocused { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventKey.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventKey.gen.cs index e25f754f7..40e0e5413 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventKey.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventKey.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiInputEventKey { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMouseButton.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMouseButton.gen.cs index 23395aa53..6abccd74b 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMouseButton.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMouseButton.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiInputEventMouseButton { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMousePos.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMousePos.gen.cs index 9e5566e2b..063eb235d 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMousePos.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMousePos.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiInputEventMousePos { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMouseViewport.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMouseViewport.gen.cs index c37c19925..a6be31c99 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMouseViewport.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMouseViewport.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiInputEventMouseViewport { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMouseWheel.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMouseWheel.gen.cs index c5f5c29a5..982cb5f11 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMouseWheel.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventMouseWheel.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiInputEventMouseWheel { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventPtr.gen.cs index c4469ec26..c7db8ae43 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiInputEventPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventText.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventText.gen.cs index 4a7d398e5..a4c919876 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventText.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputEventText.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiInputEventText { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextCallbackData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextCallbackData.gen.cs index 511749bcb..4400d5842 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextCallbackData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextCallbackData.gen.cs @@ -49,4 +49,3 @@ public unsafe partial struct ImGuiInputTextCallbackData } } // DISCARDED: InsertChars - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextCallbackDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextCallbackDataPtr.gen.cs index 849c60ef4..3938d873a 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextCallbackDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextCallbackDataPtr.gen.cs @@ -34,4 +34,3 @@ public unsafe partial struct ImGuiInputTextCallbackDataPtr } } // DISCARDED: InsertChars - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextState.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextState.gen.cs index cf0f2a122..e6af29db3 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextState.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextState.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiInputTextState } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextStatePtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextStatePtr.gen.cs index 0d1acd6cb..9419473a6 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextStatePtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiInputTextStatePtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiInputTextStatePtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiKeyData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiKeyData.gen.cs index 5c5ca435d..0cb17bf73 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiKeyData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiKeyData.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiKeyData { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiKeyDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiKeyDataPtr.gen.cs index 5d4a4ac3e..2ee1fdc9b 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiKeyDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiKeyDataPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiKeyDataPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiLastItemData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiLastItemData.gen.cs index 3963c3867..a389aa3f5 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiLastItemData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiLastItemData.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiLastItemData } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiLastItemDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiLastItemDataPtr.gen.cs index 03cd3669e..28e37f5d4 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiLastItemDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiLastItemDataPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiLastItemDataPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipper.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipper.gen.cs index 2cb71e3ee..2d49b94b1 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipper.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipper.gen.cs @@ -55,4 +55,3 @@ public unsafe partial struct ImGuiListClipper } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperData.gen.cs index 09b338fc3..ee92dbd61 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperData.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiListClipperData } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperDataPtr.gen.cs index 6b1fe9aef..4b249f2dc 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperDataPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiListClipperDataPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperPtr.gen.cs index eda25fa58..6d04b48be 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperPtr.gen.cs @@ -37,4 +37,3 @@ public unsafe partial struct ImGuiListClipperPtr return ret != 0; } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperRange.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperRange.gen.cs index ece752c83..5d9e1c544 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperRange.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperRange.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiListClipperRange { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperRangePtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperRangePtr.gen.cs index 439a14f3b..bab538f31 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperRangePtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiListClipperRangePtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiListClipperRangePtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMenuColumns.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMenuColumns.gen.cs index 9ad4d2bae..51aa6013a 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMenuColumns.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMenuColumns.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiMenuColumns } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMenuColumnsPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMenuColumnsPtr.gen.cs index 717db2fdf..86005fe6e 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMenuColumnsPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMenuColumnsPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiMenuColumnsPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMetricsConfig.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMetricsConfig.gen.cs index 876fb9871..b7c3a5a12 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMetricsConfig.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMetricsConfig.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiMetricsConfig } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMetricsConfigPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMetricsConfigPtr.gen.cs index 5d4278371..45cfe2e98 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMetricsConfigPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiMetricsConfigPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiMetricsConfigPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNavItemData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNavItemData.gen.cs index 757bc3a0a..0bff7c9d0 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNavItemData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNavItemData.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiNavItemData } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNavItemDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNavItemDataPtr.gen.cs index 8581bb0a2..d46aee8fe 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNavItemDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNavItemDataPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiNavItemDataPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextItemData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextItemData.gen.cs index 9d3da6e5e..cc6c32cf0 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextItemData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextItemData.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiNextItemData } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextItemDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextItemDataPtr.gen.cs index d0fefe239..3e6e3fada 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextItemDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextItemDataPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiNextItemDataPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextWindowData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextWindowData.gen.cs index c38fb2e3a..5a06766e0 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextWindowData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextWindowData.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiNextWindowData } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextWindowDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextWindowDataPtr.gen.cs index 505aca780..56cf1555b 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextWindowDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiNextWindowDataPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiNextWindowDataPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumnData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumnData.gen.cs index 5d65d8b6a..02b073645 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumnData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumnData.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiOldColumnData } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumnDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumnDataPtr.gen.cs index a9eb6cfc0..ef4bb0daf 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumnDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumnDataPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiOldColumnDataPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumns.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumns.gen.cs index 458d36981..3cba17d3e 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumns.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumns.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiOldColumns } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumnsPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumnsPtr.gen.cs index 8868ed7d5..f0d82030d 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumnsPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOldColumnsPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiOldColumnsPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOnceUponAFrame.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOnceUponAFrame.gen.cs index d27329c85..4e84c59f0 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOnceUponAFrame.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOnceUponAFrame.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiOnceUponAFrame } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOnceUponAFramePtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOnceUponAFramePtr.gen.cs index 494d48914..4853558f6 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOnceUponAFramePtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiOnceUponAFramePtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiOnceUponAFramePtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPayload.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPayload.gen.cs index a2db21747..e9adbcba0 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPayload.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPayload.gen.cs @@ -43,4 +43,3 @@ public unsafe partial struct ImGuiPayload } } // DISCARDED: IsDataType - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPayloadPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPayloadPtr.gen.cs index 5dec38288..a010ff422 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPayloadPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPayloadPtr.gen.cs @@ -31,4 +31,3 @@ public unsafe partial struct ImGuiPayloadPtr } } // DISCARDED: IsDataType - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformIO.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformIO.gen.cs index d0ab67a8e..a33681fdb 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformIO.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformIO.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiPlatformIO } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformIOPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformIOPtr.gen.cs index 6614e1532..a9ca00425 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformIOPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformIOPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiPlatformIOPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformImeData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformImeData.gen.cs index 7bcbdd907..8728a5184 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformImeData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformImeData.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiPlatformImeData } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformImeDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformImeDataPtr.gen.cs index a330f293b..faf99c987 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformImeDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformImeDataPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiPlatformImeDataPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformMonitor.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformMonitor.gen.cs index 2b87b2c13..553d7f7d9 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformMonitor.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformMonitor.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiPlatformMonitor } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformMonitorPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformMonitorPtr.gen.cs index 74dbb3b52..1cd0a5cae 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformMonitorPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPlatformMonitorPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiPlatformMonitorPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPopupData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPopupData.gen.cs index 04ba205e8..b745cc947 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPopupData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPopupData.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiPopupData } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPopupDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPopupDataPtr.gen.cs index 5ca38dad4..de8330596 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPopupDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPopupDataPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiPopupDataPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPtrOrIndex.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPtrOrIndex.gen.cs index 08ba47b06..2fc79140e 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPtrOrIndex.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPtrOrIndex.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiPtrOrIndex } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPtrOrIndexPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPtrOrIndexPtr.gen.cs index bd65f96f5..26c50dd84 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPtrOrIndexPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiPtrOrIndexPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiPtrOrIndexPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiSettingsHandler.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiSettingsHandler.gen.cs index a67ecb5e3..90a498d31 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiSettingsHandler.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiSettingsHandler.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiSettingsHandler } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiSettingsHandlerPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiSettingsHandlerPtr.gen.cs index 84d3e3e2b..bfeea08f0 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiSettingsHandlerPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiSettingsHandlerPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiSettingsHandlerPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiShrinkWidthItem.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiShrinkWidthItem.gen.cs index e7d06ab7d..ec6c4569e 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiShrinkWidthItem.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiShrinkWidthItem.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiShrinkWidthItem { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiShrinkWidthItemPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiShrinkWidthItemPtr.gen.cs index e6fa465f9..167a472a2 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiShrinkWidthItemPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiShrinkWidthItemPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiShrinkWidthItemPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiSizeCallbackData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiSizeCallbackData.gen.cs index aa3b74526..8b1bedb1d 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiSizeCallbackData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiSizeCallbackData.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiSizeCallbackData { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackLevelInfo.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackLevelInfo.gen.cs index 4d5b8d9cd..61ee6e36e 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackLevelInfo.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackLevelInfo.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiStackLevelInfo } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackLevelInfoPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackLevelInfoPtr.gen.cs index 9a2c46bc3..9c3238033 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackLevelInfoPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackLevelInfoPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiStackLevelInfoPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackSizes.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackSizes.gen.cs index 700515c21..ccb69adda 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackSizes.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackSizes.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiStackSizes } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackSizesPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackSizesPtr.gen.cs index 08ae5176c..7e1454016 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackSizesPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackSizesPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiStackSizesPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackTool.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackTool.gen.cs index 240cb054b..7f0c4d468 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackTool.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackTool.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiStackTool } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackToolPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackToolPtr.gen.cs index 6c412fe2a..6fc491bc1 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackToolPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStackToolPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiStackToolPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStorage.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStorage.gen.cs index dd22118e0..592d7d388 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStorage.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStorage.gen.cs @@ -121,4 +121,3 @@ public unsafe partial struct ImGuiStorage // DISCARDED: GetFloatRef // DISCARDED: GetIntRef // DISCARDED: GetVoidPtrRef - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStoragePair.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStoragePair.gen.cs index 7db41165a..4bd1280cf 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStoragePair.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStoragePair.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiStoragePair } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStoragePairPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStoragePairPtr.gen.cs index bf5d4c320..92dbebcbd 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStoragePairPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStoragePairPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiStoragePairPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStoragePtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStoragePtr.gen.cs index 8ce6b61e9..5ba04c913 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStoragePtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStoragePtr.gen.cs @@ -79,4 +79,3 @@ public unsafe partial struct ImGuiStoragePtr // DISCARDED: GetFloatRef // DISCARDED: GetIntRef // DISCARDED: GetVoidPtrRef - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStyle.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStyle.gen.cs index f2c16041a..ce7dbb0ca 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStyle.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStyle.gen.cs @@ -26,4 +26,3 @@ public unsafe partial struct ImGuiStyle } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStyleMod.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStyleMod.gen.cs index f73cbbd84..0376004f0 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStyleMod.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStyleMod.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiStyleMod } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStyleModPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStyleModPtr.gen.cs index b67acd04b..f76899c44 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStyleModPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStyleModPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiStyleModPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStylePtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStylePtr.gen.cs index 5690d0872..d66d02583 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStylePtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiStylePtr.gen.cs @@ -20,4 +20,3 @@ public unsafe partial struct ImGuiStylePtr ImGuiNative.ScaleAllSizes(Handle, scaleFactor); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabBar.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabBar.gen.cs index aad4b15a0..673bc61e0 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabBar.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabBar.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiTabBar } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabBarPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabBarPtr.gen.cs index 35a11571f..fbcd4beee 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabBarPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabBarPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiTabBarPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabItem.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabItem.gen.cs index d9be5cde2..17496ca38 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabItem.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabItem.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiTabItem } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabItemPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabItemPtr.gen.cs index 03f032ae7..e70f10d88 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabItemPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTabItemPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiTabItemPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTable.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTable.gen.cs index c1e238064..151b6db34 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTable.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTable.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiTable { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableCellData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableCellData.gen.cs index e3e33c821..8b9462e0c 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableCellData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableCellData.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiTableCellData { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableCellDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableCellDataPtr.gen.cs index 7ed59568a..ff519a6e7 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableCellDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableCellDataPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiTableCellDataPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumn.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumn.gen.cs index 1d8a792a9..97ab4bdb8 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumn.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumn.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiTableColumn } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnPtr.gen.cs index b58c6bdfd..e82da10a4 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiTableColumnPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSettings.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSettings.gen.cs index 4bdf2514c..1f4e8c606 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSettings.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSettings.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiTableColumnSettings } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSettingsPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSettingsPtr.gen.cs index f3eebda34..03a961781 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSettingsPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSettingsPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiTableColumnSettingsPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSortSpecs.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSortSpecs.gen.cs index 560c0479f..bbe570f8d 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSortSpecs.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSortSpecs.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiTableColumnSortSpecs } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSortSpecsPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSortSpecsPtr.gen.cs index a5474f37a..9c0773d8c 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSortSpecsPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnSortSpecsPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiTableColumnSortSpecsPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnsSettings.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnsSettings.gen.cs index 33246fa87..d4c36b46d 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnsSettings.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableColumnsSettings.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiTableColumnsSettings { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableInstanceData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableInstanceData.gen.cs index 6be8125aa..f66b1912c 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableInstanceData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableInstanceData.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiTableInstanceData } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableInstanceDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableInstanceDataPtr.gen.cs index 2f0f44bb9..6d9420e8f 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableInstanceDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableInstanceDataPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiTableInstanceDataPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTablePtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTablePtr.gen.cs index 91f0569ee..c8ce06302 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTablePtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTablePtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiTablePtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSettings.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSettings.gen.cs index 8f750cf54..bfc10e5c7 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSettings.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSettings.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiTableSettings } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSettingsPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSettingsPtr.gen.cs index 00cf67d70..b409b21fb 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSettingsPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSettingsPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiTableSettingsPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSortSpecs.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSortSpecs.gen.cs index e32e44949..a26afca38 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSortSpecs.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSortSpecs.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiTableSortSpecs } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSortSpecsPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSortSpecsPtr.gen.cs index 3ae71e11e..da0273486 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSortSpecsPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableSortSpecsPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiTableSortSpecsPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableTempData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableTempData.gen.cs index fa2715038..207d45a38 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableTempData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableTempData.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiTableTempData } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableTempDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableTempDataPtr.gen.cs index e950701b3..0a43ff19a 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableTempDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTableTempDataPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiTableTempDataPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextBuffer.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextBuffer.gen.cs index 902472c02..1a634a6fe 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextBuffer.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextBuffer.gen.cs @@ -58,4 +58,3 @@ public unsafe partial struct ImGuiTextBuffer // DISCARDED: c_strS // DISCARDED: end // DISCARDED: endS - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextBufferPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextBufferPtr.gen.cs index f1a03482c..4dab0fbcb 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextBufferPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextBufferPtr.gen.cs @@ -43,4 +43,3 @@ public unsafe partial struct ImGuiTextBufferPtr // DISCARDED: c_strS // DISCARDED: end // DISCARDED: endS - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextFilter.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextFilter.gen.cs index 6acd610ff..8f0876285 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextFilter.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextFilter.gen.cs @@ -43,4 +43,3 @@ public unsafe partial struct ImGuiTextFilter } // DISCARDED: Draw // DISCARDED: PassFilter - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextFilterPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextFilterPtr.gen.cs index 2470cb0f6..b2d54c5f3 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextFilterPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextFilterPtr.gen.cs @@ -31,4 +31,3 @@ public unsafe partial struct ImGuiTextFilterPtr } // DISCARDED: Draw // DISCARDED: PassFilter - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextRange.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextRange.gen.cs index 0902d8254..334e360b8 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextRange.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextRange.gen.cs @@ -44,4 +44,3 @@ public unsafe partial struct ImGuiTextRange } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextRangePtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextRangePtr.gen.cs index 8dfdc67f3..0c5aa5703 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextRangePtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiTextRangePtr.gen.cs @@ -32,4 +32,3 @@ public unsafe partial struct ImGuiTextRangePtr } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewport.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewport.gen.cs index ee2165eb6..485f94b88 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewport.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewport.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiViewport } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportP.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportP.gen.cs index f505fd913..436ba290b 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportP.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportP.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiViewportP { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPPtr.gen.cs index 23d081673..4f9f7f755 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiViewportPPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPPtrPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPPtrPtr.gen.cs index 100e5a6aa..5b69bb805 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPPtrPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPPtrPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiViewportPPtrPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPtr.gen.cs index 9b4e99572..bd7c55bc7 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiViewportPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPtrPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPtrPtr.gen.cs index 932dd2fb7..1cffdfe55 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPtrPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiViewportPtrPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiViewportPtrPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindow.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindow.gen.cs index 6a14c7f67..b42187061 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindow.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindow.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiWindow { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowClass.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowClass.gen.cs index 8d5c64f6a..a001c98bf 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowClass.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowClass.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiWindowClass } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowClassPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowClassPtr.gen.cs index 87c8f02fc..c3867b206 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowClassPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowClassPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiWindowClassPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowDockStyle.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowDockStyle.gen.cs index 769cbe51a..499a41947 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowDockStyle.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowDockStyle.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiWindowDockStyle { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowPtr.gen.cs index ab091e3e0..11a8f7b8d 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiWindowPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowPtrPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowPtrPtr.gen.cs index 4ea675d77..3e9395633 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowPtrPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowPtrPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiWindowPtrPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowSettings.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowSettings.gen.cs index 0dd1f8ab2..eb77df86f 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowSettings.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowSettings.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImGuiWindowSettings } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowSettingsPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowSettingsPtr.gen.cs index 6b646a3b6..9b04070a2 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowSettingsPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowSettingsPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImGuiWindowSettingsPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowStackData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowStackData.gen.cs index 5487fa658..c123470d4 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowStackData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowStackData.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiWindowStackData { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowStackDataPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowStackDataPtr.gen.cs index 3260ca47f..15948461d 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowStackDataPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowStackDataPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiWindowStackDataPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowTempData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowTempData.gen.cs index a9c99fb2f..5f56b954a 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowTempData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImGuiWindowTempData.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiWindowTempData { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImPoolImGuiTabBar.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImPoolImGuiTabBar.gen.cs index cec5bc071..6a8ed50b0 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImPoolImGuiTabBar.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImPoolImGuiTabBar.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImPoolImGuiTabBar { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImPoolImGuiTable.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImPoolImGuiTable.gen.cs index d4880dc8c..9058a29c8 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImPoolImGuiTable.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImPoolImGuiTable.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImPoolImGuiTable { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImRect.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImRect.gen.cs index 1277235a8..9caacdf26 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImRect.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImRect.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImRect } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImRectPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImRectPtr.gen.cs index c15383bb2..32e743aa6 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImRectPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImRectPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImRectPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImSpanImGuiTableCellData.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImSpanImGuiTableCellData.gen.cs index a712caff8..80cdff589 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImSpanImGuiTableCellData.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImSpanImGuiTableCellData.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImSpanImGuiTableCellData { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImSpanImGuiTableColumn.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImSpanImGuiTableColumn.gen.cs index e20ac0509..c37d391a9 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImSpanImGuiTableColumn.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImSpanImGuiTableColumn.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImSpanImGuiTableColumn { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImSpanImGuiTableColumnIdx.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImSpanImGuiTableColumnIdx.gen.cs index 446a9134a..6ebd9bdb6 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImSpanImGuiTableColumnIdx.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImSpanImGuiTableColumnIdx.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImSpanImGuiTableColumnIdx { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec1.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec1.gen.cs index 3b5377e44..0f3f8d98d 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec1.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec1.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImVec1 } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec1Ptr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec1Ptr.gen.cs index 78d47bf9d..f095da0be 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec1Ptr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec1Ptr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImVec1Ptr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec2Ih.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec2Ih.gen.cs index c110b6293..9aed95e7d 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec2Ih.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec2Ih.gen.cs @@ -19,4 +19,3 @@ public unsafe partial struct ImVec2Ih } } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec2IhPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec2IhPtr.gen.cs index 6f7759bdb..a0432cbd8 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec2IhPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/ImVec2IhPtr.gen.cs @@ -16,4 +16,3 @@ public unsafe partial struct ImVec2IhPtr ImGuiNative.Destroy(Handle); } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/STBTexteditState.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/STBTexteditState.gen.cs index a2012943e..1cf81eaba 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/STBTexteditState.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/STBTexteditState.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct STBTexteditState { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbTexteditRow.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbTexteditRow.gen.cs index d561cc218..574e7498d 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbTexteditRow.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbTexteditRow.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct StbTexteditRow { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbUndoRecord.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbUndoRecord.gen.cs index b38b7476b..c1a39765e 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbUndoRecord.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbUndoRecord.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct StbUndoRecord { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbUndoState.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbUndoState.gen.cs index 138d0ed24..91c0fa2ef 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbUndoState.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbUndoState.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct StbUndoState { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbttPackContext.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbttPackContext.gen.cs index 8da0c50b0..7d067c47f 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbttPackContext.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbttPackContext.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct StbttPackContext { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbttPackContextPtr.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbttPackContextPtr.gen.cs index 142106368..8b4c5cab7 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbttPackContextPtr.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Generated/Structs/StbttPackContextPtr.gen.cs @@ -12,4 +12,3 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct StbttPackContextPtr { } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Internals/Functions/ImGuiP.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Internals/Functions/ImGuiP.gen.cs index 08148462e..bccfeb412 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Internals/Functions/ImGuiP.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Internals/Functions/ImGuiP.gen.cs @@ -6594,4 +6594,3 @@ public unsafe partial class ImGuiP // DISCARDED: internal static byte TempInputScalarNative(ImRect bb, uint id, byte* label, ImGuiDataType dataType, void* pData, byte* format, void* pClampMin, void* pClampMax) // DISCARDED: internal static void TextExNative(byte* text, byte* textEnd, ImGuiTextFlags flags) // DISCARDED: internal static byte TreeNodeBehaviorNative(uint id, ImGuiTreeNodeFlags flags, byte* label, byte* labelEnd) - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Internals/Functions/ImGuiPNative.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Internals/Functions/ImGuiPNative.gen.cs index 20093f7a0..8c21faf48 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Internals/Functions/ImGuiPNative.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Internals/Functions/ImGuiPNative.gen.cs @@ -4065,4 +4065,3 @@ public unsafe partial class ImGuiPNative } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Manual/Functions/ImGui.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Manual/Functions/ImGui.gen.cs index 02a8e833b..f4a51c35f 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Manual/Functions/ImGui.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Manual/Functions/ImGui.gen.cs @@ -24,4 +24,3 @@ public unsafe partial class ImGui // DISCARDED: internal static byte InputTextMultilineNative(byte* label, byte* buf, nuint bufSize, Vector2 size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* userData) // DISCARDED: internal static byte InputTextWithHintNative(byte* label, byte* hint, byte* buf, nuint bufSize, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* userData) // DISCARDED: internal static byte TempInputTextNative(ImRect bb, uint id, byte* label, byte* buf, int bufSize, ImGuiInputTextFlags flags) - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Manual/Functions/ImGuiNative.gen.cs b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Manual/Functions/ImGuiNative.gen.cs index 92632fb9e..04c166e66 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Manual/Functions/ImGuiNative.gen.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/Generated/Manual/Functions/ImGuiNative.gen.cs @@ -89,4 +89,3 @@ public unsafe partial class ImGuiNative } } - diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.ColorEditPicker.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.ColorEditPicker.cs index 655083627..9cfdd970c 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.ColorEditPicker.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.ColorEditPicker.cs @@ -13,7 +13,7 @@ public static unsafe partial class ImGui fixed (Vector3* colPtr = &col) { var res = ImGuiNative.ColorEdit3(labelPtr, &colPtr->X, flags) != 0; - label.Dispose(); + label.Recycle(); return res; } } @@ -25,7 +25,7 @@ public static unsafe partial class ImGui fixed (Vector4* colPtr = &col) { var res = ImGuiNative.ColorEdit4(labelPtr, &colPtr->X, flags) != 0; - label.Dispose(); + label.Recycle(); return res; } } @@ -37,7 +37,7 @@ public static unsafe partial class ImGui fixed (Vector3* colPtr = &col) { var res = ImGuiNative.ColorPicker3(labelPtr, &colPtr->X, flags) != 0; - label.Dispose(); + label.Recycle(); return res; } } @@ -49,7 +49,7 @@ public static unsafe partial class ImGui fixed (Vector4* colPtr = &col) { var res = ImGuiNative.ColorPicker4(labelPtr, &colPtr->X, flags, null) != 0; - label.Dispose(); + label.Recycle(); return res; } } @@ -62,7 +62,7 @@ public static unsafe partial class ImGui fixed (Vector4* refColPtr = &refCol) { var res = ImGuiNative.ColorPicker4(labelPtr, &colPtr->X, flags, &refColPtr->X) != 0; - label.Dispose(); + label.Recycle(); return res; } } @@ -74,7 +74,7 @@ public static unsafe partial class ImGui fixed (Vector4* refColPtr = &refCol) { var res = ImGuiNative.ColorPicker4(labelPtr, &colPtr->X, ImGuiColorEditFlags.None, &refColPtr->X) != 0; - label.Dispose(); + label.Recycle(); return res; } } diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.ComboAndList.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.ComboAndList.cs index 603019d7d..ed261a694 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.ComboAndList.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.ComboAndList.cs @@ -15,7 +15,25 @@ public static unsafe partial class ImGui public delegate ImU8String PopulateAutoUtf8BufferRefContextDelegate(scoped ref T context, int index) where T : allows ref struct; - [OverloadResolutionPriority(2)] + [OverloadResolutionPriority(8)] + public static bool Combo( + ImU8String label, ref int currentItem, ImU8String itemsSeparatedByZeros, int popupMaxHeightInItems = -1) + { + if (!itemsSeparatedByZeros.Span.EndsWith("\0\0"u8)) + itemsSeparatedByZeros.AppendFormatted("\0\0"u8); + + fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) + fixed (int* currentItemPtr = ¤tItem) + fixed (byte* itemsSeparatedByZerosPtr = itemsSeparatedByZeros) + { + var r = ImGuiNative.Combo(labelPtr, currentItemPtr, itemsSeparatedByZerosPtr, popupMaxHeightInItems) != 0; + label.Recycle(); + itemsSeparatedByZeros.Recycle(); + return r; + } + } + + [OverloadResolutionPriority(7)] public static bool Combo( ImU8String label, ref int currentItem, ReadOnlySpan items, int popupMaxHeightInItems = -1) => Combo( @@ -26,7 +44,7 @@ public static unsafe partial class ImGui items.Length, popupMaxHeightInItems); - [OverloadResolutionPriority(3)] + [OverloadResolutionPriority(6)] public static bool Combo( ImU8String label, ref int currentItem, scoped in T items, int popupMaxHeightInItems = -1) where T : IList => @@ -38,7 +56,7 @@ public static unsafe partial class ImGui items.Count, popupMaxHeightInItems); - [OverloadResolutionPriority(4)] + [OverloadResolutionPriority(5)] public static bool Combo( ImU8String label, ref int currentItem, IReadOnlyList items, int popupMaxHeightInItems = -1) => Combo( @@ -49,7 +67,7 @@ public static unsafe partial class ImGui items.Count, popupMaxHeightInItems); - [OverloadResolutionPriority(5)] + [OverloadResolutionPriority(4)] public static bool Combo( ImU8String label, ref int currentItem, ReadOnlySpan items, Func toString, int popupMaxHeightInItems = -1) @@ -65,7 +83,7 @@ public static unsafe partial class ImGui popupMaxHeightInItems); } - [OverloadResolutionPriority(6)] + [OverloadResolutionPriority(3)] public static bool Combo( ImU8String label, ref int currentItem, scoped in TList items, Func toString, int popupMaxHeightInItems = -1) @@ -78,7 +96,7 @@ public static unsafe partial class ImGui items.Count, popupMaxHeightInItems); - [OverloadResolutionPriority(7)] + [OverloadResolutionPriority(2)] public static bool Combo( ImU8String label, ref int currentItem, IReadOnlyList items, Func toString, int popupMaxHeightInItems = -1) => @@ -90,24 +108,6 @@ public static unsafe partial class ImGui items.Count, popupMaxHeightInItems); - [OverloadResolutionPriority(1)] - public static bool Combo( - ImU8String label, ref int currentItem, ImU8String itemsSeparatedByZeros, int popupMaxHeightInItems = -1) - { - if (!itemsSeparatedByZeros.Span.EndsWith("\0\0"u8)) - itemsSeparatedByZeros.AppendFormatted("\0\0"u8); - - fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) - fixed (int* currentItemPtr = ¤tItem) - fixed (byte* itemsSeparatedByZerosPtr = itemsSeparatedByZeros.Span) - { - var r = ImGuiNative.Combo(labelPtr, currentItemPtr, itemsSeparatedByZerosPtr, popupMaxHeightInItems) != 0; - label.Dispose(); - itemsSeparatedByZeros.Dispose(); - return r; - } - } - public static bool Combo( ImU8String label, ref int currentItem, PopulateAutoUtf8BufferInContextDelegate itemsGetter, scoped in TContext context, int itemsCount, int popupMaxHeightInItems = -1) @@ -129,8 +129,8 @@ public static unsafe partial class ImGui &dataBuffer, itemsCount, popupMaxHeightInItems) != 0; - label.Dispose(); - textBuffer.Dispose(); + label.Recycle(); + textBuffer.Recycle(); return r; } } @@ -156,8 +156,8 @@ public static unsafe partial class ImGui &dataBuffer, itemsCount, popupMaxHeightInItems) != 0; - label.Dispose(); - textBuffer.Dispose(); + label.Recycle(); + textBuffer.Recycle(); return r; } } @@ -181,8 +181,8 @@ public static unsafe partial class ImGui &dataBuffer, itemsCount, popupMaxHeightInItems) != 0; - label.Dispose(); - textBuffer.Dispose(); + label.Recycle(); + textBuffer.Recycle(); return r; } } @@ -282,8 +282,8 @@ public static unsafe partial class ImGui &dataBuffer, itemsCount, heightInItems) != 0; - label.Dispose(); - textBuffer.Dispose(); + label.Recycle(); + textBuffer.Recycle(); return r; } } @@ -309,8 +309,8 @@ public static unsafe partial class ImGui &dataBuffer, itemsCount, heightInItems) != 0; - label.Dispose(); - textBuffer.Dispose(); + label.Recycle(); + textBuffer.Recycle(); return r; } } @@ -334,8 +334,8 @@ public static unsafe partial class ImGui &dataBuffer, itemsCount, heightInItems) != 0; - label.Dispose(); - textBuffer.Dispose(); + label.Recycle(); + textBuffer.Recycle(); return r; } } @@ -345,11 +345,11 @@ public static unsafe partial class ImGui { #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ref var s = ref PointerTuple.From, ImU8String, object>(data); - s.Item2.Dispose(); + s.Item2.Recycle(); s.Item2 = s.Item1.Invoke(ref s.Item3, index); if (s.Item2.IsNull) return false; - *text = (byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in s.Item2.Span[0])); + *text = (byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in s.Item2.GetPinnableNullTerminatedReference())); return true; #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type } @@ -359,11 +359,11 @@ public static unsafe partial class ImGui { #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ref var s = ref PointerTuple.From, ImU8String, object>(data); - s.Item2.Dispose(); + s.Item2.Recycle(); s.Item2 = s.Item1.Invoke(s.Item3, index); if (s.Item2.IsNull) return false; - *text = (byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in s.Item2.Span[0])); + *text = (byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in s.Item2.GetPinnableNullTerminatedReference())); return true; #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type } @@ -373,11 +373,11 @@ public static unsafe partial class ImGui { #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type ref var s = ref PointerTuple.From(data); - s.Item2.Dispose(); + s.Item2.Recycle(); s.Item2 = s.Item1.Invoke(index); if (s.Item2.IsNull) return false; - *text = (byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in s.Item2.Span[0])); + *text = (byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in s.Item2.GetPinnableNullTerminatedReference())); return true; #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type } diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.DragScalar.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.DragScalar.cs index 13c06ad2b..665fa434f 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.DragScalar.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.DragScalar.cs @@ -309,8 +309,8 @@ public static unsafe partial class ImGui fixed (T* vMaxPtr = &vMax) { var res = ImGuiNative.DragScalar(labelPtr, dataType, vPtr, vSpeed, vMinPtr, vMaxPtr, formatPtr, flags) != 0; - label.Dispose(); - format.Dispose(); + label.Recycle(); + format.Recycle(); return res; } } @@ -336,8 +336,8 @@ public static unsafe partial class ImGui vMaxPtr, formatPtr, flags) != 0; - label.Dispose(); - format.Dispose(); + label.Recycle(); + format.Recycle(); return res; } } @@ -362,8 +362,8 @@ public static unsafe partial class ImGui vMaxPtr, formatPtr, flags) != 0; - label.Dispose(); - format.Dispose(); + label.Recycle(); + format.Recycle(); return res; } } @@ -389,8 +389,8 @@ public static unsafe partial class ImGui vMaxPtr, formatPtr, flags) != 0; - label.Dispose(); - format.Dispose(); + label.Recycle(); + format.Recycle(); return res; } } @@ -418,9 +418,9 @@ public static unsafe partial class ImGui formatPtr, formatMaxPtr, flags); - label.Dispose(); - format.Dispose(); - formatMax.Dispose(); + label.Recycle(); + format.Recycle(); + formatMax.Recycle(); return res != 0; } } @@ -448,9 +448,9 @@ public static unsafe partial class ImGui formatPtr, formatMaxPtr, flags); - label.Dispose(); - format.Dispose(); - formatMax.Dispose(); + label.Recycle(); + format.Recycle(); + formatMax.Recycle(); return res != 0; } } diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.InputScalar.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.InputScalar.cs index 7138d8a92..fb86096ff 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.InputScalar.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.InputScalar.cs @@ -270,12 +270,12 @@ public static unsafe partial class ImGui labelPtr, dataType, dataPtr, - stepPtr, - stepFastPtr, + step > T.Zero ? stepPtr : null, + stepFast > T.Zero ? stepFastPtr : null, formatPtr, flags) != 0; - label.Dispose(); - format.Dispose(); + label.Recycle(); + format.Recycle(); return res; } } @@ -298,12 +298,12 @@ public static unsafe partial class ImGui dataType, dataPtr, data.Length, - stepPtr, - stepFastPtr, + step > T.Zero ? stepPtr : null, + stepFast > T.Zero ? stepFastPtr : null, formatPtr, flags) != 0; - label.Dispose(); - format.Dispose(); + label.Recycle(); + format.Recycle(); return res; } } @@ -325,12 +325,12 @@ public static unsafe partial class ImGui labelPtr, GetImGuiDataType(), dataPtr, - stepPtr, - stepFastPtr, + step > T.Zero ? stepPtr : null, + stepFast > T.Zero ? stepFastPtr : null, formatPtr, flags) != 0; - label.Dispose(); - format.Dispose(); + label.Recycle(); + format.Recycle(); return res; } } @@ -353,14 +353,13 @@ public static unsafe partial class ImGui GetImGuiDataType(), dataPtr, data.Length, - stepPtr, - stepFastPtr, + step > T.Zero ? stepPtr : null, + stepFast > T.Zero ? stepFastPtr : null, formatPtr, flags) != 0; - label.Dispose(); - format.Dispose(); + label.Recycle(); + format.Recycle(); return res; } } - } diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Manual.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Manual.cs index 7b0aefe97..89b3cc3d6 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Manual.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Manual.cs @@ -26,7 +26,7 @@ public unsafe partial class ImGui fixed (byte* formatPtr = &format.GetPinnableNullTerminatedReference()) { var len = ImGuiNative.DataTypeFormatString(bufPtr, buf.Length, dataType, &data, formatPtr); - format.Dispose(); + format.Recycle(); return buf[..len]; } } @@ -39,7 +39,7 @@ public unsafe partial class ImGui fixed (byte* formatPtr = &format.GetPinnableNullTerminatedReference()) fixed (byte* bufPtr = buf) ImGuiNative.ImParseFormatTrimDecorations(formatPtr, bufPtr, (nuint)buf.Length); - format.Dispose(); + format.Recycle(); var nul = buf.IndexOf((byte)0); return nul == -1 ? buf : buf[..nul]; } @@ -77,6 +77,8 @@ public unsafe partial class ImGui public delegate int ImGuiInputTextCallbackDelegate(scoped ref ImGuiInputTextCallbackData data); + public delegate int ImGuiInputTextCallbackPtrDelegate(ImGuiInputTextCallbackDataPtr data); + public delegate int ImGuiInputTextCallbackRefContextDelegate( scoped ref ImGuiInputTextCallbackData data, scoped ref TContext context); @@ -92,6 +94,14 @@ public unsafe partial class ImGui return InputTextEx(label, default, buf, default, flags, callback); } + public static bool InputText( + ImU8String label, Span buf, ImGuiInputTextFlags flags, ImGuiInputTextCallbackPtrDelegate? callback) + { + if ((flags & (ImGuiInputTextFlags)ImGuiInputTextFlagsPrivate.Multiline) != ImGuiInputTextFlags.None) + throw new ArgumentOutOfRangeException(nameof(flags), flags, "Multiline must not be set"); + return InputTextEx(label, default, buf, default, flags, callback); + } + public static bool InputText( ImU8String label, Span buf, ImGuiInputTextFlags flags, ImGuiInputTextCallbackRefContextDelegate callback, scoped ref TContext context) @@ -112,15 +122,27 @@ public unsafe partial class ImGui public static bool InputText( ImU8String label, scoped ref string buf, int maxLength = ImU8String.AllocFreeBufferSize, - ImGuiInputTextFlags flags = ImGuiInputTextFlags.None, - ImGuiInputTextCallbackDelegate? callback = null) + ImGuiInputTextFlags flags = ImGuiInputTextFlags.None, ImGuiInputTextCallbackDelegate? callback = null) { var t = new ImU8String(buf); t.Reserve(maxLength + 1); var r = InputText(label, t.Buffer[..(maxLength + 1)], flags, callback); var i = t.Buffer.IndexOf((byte)0); buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - t.Dispose(); + t.Recycle(); + return r; + } + + public static bool InputText( + ImU8String label, scoped ref string buf, int maxLength, ImGuiInputTextFlags flags, + ImGuiInputTextCallbackPtrDelegate? callback) + { + var t = new ImU8String(buf); + t.Reserve(maxLength + 1); + var r = InputText(label, t.Buffer[..(maxLength + 1)], flags, callback); + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); + t.Recycle(); return r; } @@ -133,7 +155,7 @@ public unsafe partial class ImGui var r = InputText(label, t.Buffer[..(maxLength + 1)], flags, callback, ref context); var i = t.Buffer.IndexOf((byte)0); buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - t.Dispose(); + t.Recycle(); return r; } @@ -146,7 +168,7 @@ public unsafe partial class ImGui var r = InputText(label, t.Buffer[..(maxLength + 1)], flags, callback, in context); var i = t.Buffer.IndexOf((byte)0); buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - t.Dispose(); + t.Recycle(); return r; } @@ -170,8 +192,34 @@ public unsafe partial class ImGui flags, callback == null ? null : &InputTextCallbackStatic, callback == null ? null : &dataBuffer) != 0; - label.Dispose(); - hint.Dispose(); + label.Recycle(); + hint.Recycle(); + return r; + } + } + + public static bool InputTextEx( + ImU8String label, ImU8String hint, Span buf, Vector2 sizeArg, + ImGuiInputTextFlags flags, ImGuiInputTextCallbackPtrDelegate? callback) + { + fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) + fixed (byte* hintPtr = &hint.GetPinnableNullTerminatedReference()) + fixed (byte* bufPtr = buf) +#pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type + { + var dataBuffer = PointerTuple.Create(&callback); +#pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type + var r = ImGuiNative.InputTextEx( + labelPtr, + hintPtr, + bufPtr, + buf.Length, + sizeArg, + flags, + callback == null ? null : &InputTextCallbackPtrStatic, + callback == null ? null : &dataBuffer) != 0; + label.Recycle(); + hint.Recycle(); return r; } } @@ -197,8 +245,8 @@ public unsafe partial class ImGui flags, &InputTextCallbackRefContextStatic, &dataBuffer) != 0; - label.Dispose(); - hint.Dispose(); + label.Recycle(); + hint.Recycle(); return r; } } @@ -224,8 +272,8 @@ public unsafe partial class ImGui flags, &InputTextCallbackInContextStatic, &dataBuffer) != 0; - label.Dispose(); - hint.Dispose(); + label.Recycle(); + hint.Recycle(); return r; } } @@ -241,7 +289,20 @@ public unsafe partial class ImGui var r = InputTextEx(label, hint, t.Buffer[..(maxLength + 1)], sizeArg, flags, callback); var i = t.Buffer.IndexOf((byte)0); buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - t.Dispose(); + t.Recycle(); + return r; + } + + public static bool InputTextEx( + ImU8String label, ImU8String hint, scoped ref string buf, int maxLength, Vector2 sizeArg, + ImGuiInputTextFlags flags, ImGuiInputTextCallbackPtrDelegate? callback) + { + var t = new ImU8String(buf); + t.Reserve(maxLength + 1); + var r = InputTextEx(label, hint, t.Buffer[..(maxLength + 1)], sizeArg, flags, callback); + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); + t.Recycle(); return r; } @@ -255,7 +316,7 @@ public unsafe partial class ImGui var r = InputTextEx(label, hint, t.Buffer[..(maxLength + 1)], sizeArg, flags, callback, ref context); var i = t.Buffer.IndexOf((byte)0); buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - t.Dispose(); + t.Recycle(); return r; } @@ -269,7 +330,7 @@ public unsafe partial class ImGui var r = InputTextEx(label, hint, t.Buffer[..(maxLength + 1)], sizeArg, flags, callback, in context); var i = t.Buffer.IndexOf((byte)0); buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - t.Dispose(); + t.Recycle(); return r; } @@ -284,6 +345,17 @@ public unsafe partial class ImGui flags | (ImGuiInputTextFlags)ImGuiInputTextFlagsPrivate.Multiline, callback); + public static bool InputTextMultiline( + ImU8String label, Span buf, Vector2 size, ImGuiInputTextFlags flags, + ImGuiInputTextCallbackPtrDelegate? callback) => + InputTextEx( + label, + default, + buf, + size, + flags | (ImGuiInputTextFlags)ImGuiInputTextFlagsPrivate.Multiline, + callback); + public static bool InputTextMultiline( ImU8String label, Span buf, Vector2 size, ImGuiInputTextFlags flags, ImGuiInputTextCallbackRefContextDelegate callback, scoped ref TContext context) => @@ -318,7 +390,20 @@ public unsafe partial class ImGui var r = InputTextMultiline(label, t.Buffer[..(maxLength + 1)], size, flags, callback); var i = t.Buffer.IndexOf((byte)0); buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - t.Dispose(); + t.Recycle(); + return r; + } + + public static bool InputTextMultiline( + ImU8String label, scoped ref string buf, int maxLength, Vector2 size, ImGuiInputTextFlags flags, + ImGuiInputTextCallbackPtrDelegate? callback) + { + var t = new ImU8String(buf); + t.Reserve(maxLength + 1); + var r = InputTextMultiline(label, t.Buffer[..(maxLength + 1)], size, flags, callback); + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); + t.Recycle(); return r; } @@ -331,7 +416,7 @@ public unsafe partial class ImGui var r = InputTextMultiline(label, t.Buffer[..(maxLength + 1)], size, flags, callback, ref context); var i = t.Buffer.IndexOf((byte)0); buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - t.Dispose(); + t.Recycle(); return r; } @@ -344,7 +429,7 @@ public unsafe partial class ImGui var r = InputTextMultiline(label, t.Buffer[..(maxLength + 1)], size, flags, callback, in context); var i = t.Buffer.IndexOf((byte)0); buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - t.Dispose(); + t.Recycle(); return r; } @@ -357,6 +442,15 @@ public unsafe partial class ImGui return InputTextEx(label, hint, buf, default, flags, callback); } + public static bool InputTextWithHint( + ImU8String label, ImU8String hint, Span buf, ImGuiInputTextFlags flags, + ImGuiInputTextCallbackPtrDelegate? callback) + { + if ((flags & (ImGuiInputTextFlags)ImGuiInputTextFlagsPrivate.Multiline) != ImGuiInputTextFlags.None) + throw new ArgumentOutOfRangeException(nameof(flags), flags, "Multiline must not be set"); + return InputTextEx(label, hint, buf, default, flags, callback); + } + public static bool InputTextWithHint( ImU8String label, ImU8String hint, Span buf, ImGuiInputTextFlags flags, ImGuiInputTextCallbackRefContextDelegate callback, scoped ref TContext context) @@ -385,7 +479,20 @@ public unsafe partial class ImGui var r = InputTextWithHint(label, hint, t.Buffer[..(maxLength + 1)], flags, callback); var i = t.Buffer.IndexOf((byte)0); buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - t.Dispose(); + t.Recycle(); + return r; + } + + public static bool InputTextWithHint( + ImU8String label, ImU8String hint, scoped ref string buf, int maxLength, ImGuiInputTextFlags flags, + ImGuiInputTextCallbackPtrDelegate? callback) + { + var t = new ImU8String(buf); + t.Reserve(maxLength + 1); + var r = InputTextWithHint(label, hint, t.Buffer[..(maxLength + 1)], flags, callback); + var i = t.Buffer.IndexOf((byte)0); + buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); + t.Recycle(); return r; } @@ -398,7 +505,7 @@ public unsafe partial class ImGui var r = InputTextWithHint(label, hint, t.Buffer[..(maxLength + 1)], flags, callback, ref context); var i = t.Buffer.IndexOf((byte)0); buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - t.Dispose(); + t.Recycle(); return r; } @@ -411,7 +518,7 @@ public unsafe partial class ImGui var r = InputTextWithHint(label, hint, t.Buffer[..(maxLength + 1)], flags, callback, in context); var i = t.Buffer.IndexOf((byte)0); buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - t.Dispose(); + t.Recycle(); return r; } @@ -422,7 +529,7 @@ public unsafe partial class ImGui fixed (byte* bufPtr = buf) { var r = ImGuiNative.TempInputText(bb, id, labelPtr, bufPtr, buf.Length, flags) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -436,7 +543,7 @@ public unsafe partial class ImGui var r = TempInputText(bb, id, label, t.Buffer[..(maxLength + 1)], flags); var i = t.Buffer.IndexOf((byte)0); buf = Encoding.UTF8.GetString(i == -1 ? t.Buffer : t.Buffer[..i]); - t.Dispose(); + t.Recycle(); return r; } @@ -447,6 +554,13 @@ public unsafe partial class ImGui return dvps.Item1.Invoke(ref *data); } + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + private static int InputTextCallbackPtrStatic(ImGuiInputTextCallbackData* data) + { + ref var dvps = ref PointerTuple.From(data->UserData); + return dvps.Item1.Invoke(data); + } + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] private static int InputTextCallbackRefContextStatic(ImGuiInputTextCallbackData* data) { diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Misc.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Misc.cs index 957d72789..e7aa6cc4a 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Misc.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Misc.cs @@ -27,7 +27,7 @@ public static unsafe partial class ImGui fixed (byte* typePtr = &type.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.AcceptDragDropPayload(typePtr, flags); - type.Dispose(); + type.Recycle(); return r; } } @@ -39,7 +39,7 @@ public static unsafe partial class ImGui fixed (byte* filenamePtr = &filename.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.AddFontFromFileTTF(self, filenamePtr, sizePixels, fontCfg, glyphRanges); - filename.Dispose(); + filename.Recycle(); return r; } } @@ -56,7 +56,7 @@ public static unsafe partial class ImGui sizePixels, fontCfg, glyphRanges); - compressedFontDatabase85.Dispose(); + compressedFontDatabase85.Recycle(); return r; } } @@ -91,11 +91,12 @@ public static unsafe partial class ImGui public static void AddInputCharacter(ImGuiIOPtr self, char c) => ImGuiNative.AddInputCharacter(self, c); public static void AddInputCharacter(ImGuiIOPtr self, Rune c) => ImGuiNative.AddInputCharacter(self, (uint)c.Value); + public static void AddInputCharacters(ImGuiIOPtr self, ImU8String str) { fixed (byte* strPtr = &str.GetPinnableNullTerminatedReference()) ImGuiNative.AddInputCharactersUTF8(self.Handle, strPtr); - str.Dispose(); + str.Recycle(); } public static ref bool GetBoolRef(ImGuiStoragePtr self, uint key, bool defaultValue = false) => @@ -121,10 +122,10 @@ public static unsafe partial class ImGui public static uint GetID(ImU8String strId) { - fixed (byte* strIdPtr = strId.Span) + fixed (byte* strIdPtr = strId) { var r = ImGuiNative.GetID(strIdPtr, strIdPtr + strId.Length); - strId.Dispose(); + strId.Recycle(); return r; } } @@ -135,10 +136,10 @@ public static unsafe partial class ImGui public static void PushID(ImU8String strId) { - fixed (byte* strIdPtr = strId.Span) + fixed (byte* strIdPtr = strId) { ImGuiNative.PushID(strIdPtr, strIdPtr + strId.Length); - strId.Dispose(); + strId.Recycle(); } } diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Plot.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Plot.cs index 69b3b7dbe..d2662238c 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Plot.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Plot.cs @@ -34,8 +34,8 @@ public static unsafe partial class ImGui stride); } - label.Dispose(); - overlayText.Dispose(); + label.Recycle(); + overlayText.Recycle(); } public static void PlotHistogram( @@ -66,8 +66,8 @@ public static unsafe partial class ImGui graphSize); } - label.Dispose(); - overlayText.Dispose(); + label.Recycle(); + overlayText.Recycle(); } public static void PlotHistogram( @@ -97,8 +97,8 @@ public static unsafe partial class ImGui graphSize); } - label.Dispose(); - overlayText.Dispose(); + label.Recycle(); + overlayText.Recycle(); } public static void PlotHistogram( @@ -126,8 +126,8 @@ public static unsafe partial class ImGui graphSize); } - label.Dispose(); - overlayText.Dispose(); + label.Recycle(); + overlayText.Recycle(); } public static void PlotLines( @@ -151,8 +151,8 @@ public static unsafe partial class ImGui stride); } - label.Dispose(); - overlayText.Dispose(); + label.Recycle(); + overlayText.Recycle(); } public static void PlotLines( @@ -182,8 +182,8 @@ public static unsafe partial class ImGui graphSize); } - label.Dispose(); - overlayText.Dispose(); + label.Recycle(); + overlayText.Recycle(); } public static void PlotLines( @@ -214,8 +214,8 @@ public static unsafe partial class ImGui graphSize); } - label.Dispose(); - overlayText.Dispose(); + label.Recycle(); + overlayText.Recycle(); } public static void PlotLines( @@ -243,8 +243,8 @@ public static unsafe partial class ImGui graphSize); } - label.Dispose(); - overlayText.Dispose(); + label.Recycle(); + overlayText.Recycle(); } #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.SliderScalar.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.SliderScalar.cs index e37eaec65..20ee78ab6 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.SliderScalar.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.SliderScalar.cs @@ -277,8 +277,8 @@ public static unsafe partial class ImGui vMaxPtr, formatPtr, flags) != 0; - label.Dispose(); - format.Dispose(); + label.Recycle(); + format.Recycle(); return res; } } @@ -305,8 +305,8 @@ public static unsafe partial class ImGui vMaxPtr, formatPtr, flags) != 0; - label.Dispose(); - format.Dispose(); + label.Recycle(); + format.Recycle(); return res; } } @@ -332,8 +332,8 @@ public static unsafe partial class ImGui vMaxPtr, formatPtr, flags) != 0; - label.Dispose(); - format.Dispose(); + label.Recycle(); + format.Recycle(); return res; } } @@ -360,8 +360,8 @@ public static unsafe partial class ImGui vMaxPtr, formatPtr, flags) != 0; - label.Dispose(); - format.Dispose(); + label.Recycle(); + format.Recycle(); return res; } } @@ -383,8 +383,8 @@ public static unsafe partial class ImGui vDegreesMax, formatPtr, flags) != 0; - label.Dispose(); - format.Dispose(); + label.Recycle(); + format.Recycle(); return res; } } @@ -466,8 +466,8 @@ public static unsafe partial class ImGui { var res = ImGuiNative.VSliderScalar(labelPtr, size, dataType, dataPtr, minPtr, maxPtr, formatPtr, flags) != 0; - label.Dispose(); - format.Dispose(); + label.Recycle(); + format.Recycle(); return res; } } diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Text.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Text.cs index 297b68924..284475824 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Text.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Text.cs @@ -8,21 +8,23 @@ public static unsafe partial class ImGui { public static void AddText(ImFontGlyphRangesBuilderPtr self, ImU8String text) { - fixed (byte* textPtr = text.Span) ImGuiNative.AddText(self.Handle, textPtr, textPtr + text.Length); - text.Dispose(); + fixed (byte* textPtr = text) + ImGuiNative.AddText(self.Handle, textPtr, textPtr + text.Length); + text.Recycle(); } public static void AddText(ImDrawListPtr self, Vector2 pos, uint col, ImU8String text) { - fixed (byte* textPtr = text.Span) ImGuiNative.AddText(self.Handle, pos, col, textPtr, textPtr + text.Length); - text.Dispose(); + fixed (byte* textPtr = text) + ImGuiNative.AddText(self.Handle, pos, col, textPtr, textPtr + text.Length); + text.Recycle(); } public static void AddText( ImDrawListPtr self, ImFontPtr font, float fontSize, Vector2 pos, uint col, ImU8String text, float wrapWidth, scoped in Vector4 cpuFineClipRect) { - fixed (byte* textPtr = text.Span) + fixed (byte* textPtr = text) fixed (Vector4* cpuFineClipRectPtr = &cpuFineClipRect) ImGuiNative.AddText( self.Handle, @@ -34,22 +36,23 @@ public static unsafe partial class ImGui textPtr + text.Length, wrapWidth, cpuFineClipRectPtr); - text.Dispose(); + text.Recycle(); } public static void AddText( ImDrawListPtr self, ImFontPtr font, float fontSize, Vector2 pos, uint col, ImU8String text, float wrapWidth = 0f) { - fixed (byte* textPtr = text.Span) + fixed (byte* textPtr = text) ImGuiNative.AddText(self.Handle, font, fontSize, pos, col, textPtr, textPtr + text.Length, wrapWidth, null); - text.Dispose(); + text.Recycle(); } public static void append(this ImGuiTextBufferPtr self, ImU8String str) { - fixed (byte* strPtr = str.Span) ImGuiNative.append(self.Handle, strPtr, strPtr + str.Length); - str.Dispose(); + fixed (byte* strPtr = str) + ImGuiNative.append(self.Handle, strPtr, strPtr + str.Length); + str.Recycle(); } public static void BulletText(ImU8String text) @@ -60,8 +63,8 @@ public static unsafe partial class ImGui scoped ref readonly var style = ref g.Style; var labelSize = CalcTextSize(text.Span); var totalSize = new Vector2( - g.FontSize + (labelSize.X > 0.0f ? (labelSize.X + style.FramePadding.X * 2) : 0.0f), - labelSize.Y); // Empty text doesn't add padding + g.FontSize + (labelSize.X > 0.0f ? (labelSize.X + style.FramePadding.X * 2) : 0.0f), + labelSize.Y); // Empty text doesn't add padding var pos = window->DC.CursorPos; pos.Y += window->DC.CurrLineTextBaseOffset; ImGuiP.ItemSize(totalSize, 0.0f); @@ -82,14 +85,14 @@ public static unsafe partial class ImGui ImU8String text, bool hideTextAfterDoubleHash = false, float wrapWidth = -1.0f) { var @out = Vector2.Zero; - fixed (byte* textPtr = text.Span) + fixed (byte* textPtr = text) ImGuiNative.CalcTextSize( &@out, textPtr, textPtr + text.Length, hideTextAfterDoubleHash ? (byte)1 : (byte)0, wrapWidth); - text.Dispose(); + text.Recycle(); return @out; } @@ -97,7 +100,7 @@ public static unsafe partial class ImGui ImFontPtr self, float size, float maxWidth, float wrapWidth, ImU8String text, out int remaining) { var @out = Vector2.Zero; - fixed (byte* textPtr = text.Span) + fixed (byte* textPtr = text) { byte* remainingPtr = null; ImGuiNative.CalcTextSizeA( @@ -112,18 +115,18 @@ public static unsafe partial class ImGui remaining = (int)(remainingPtr - textPtr); } - text.Dispose(); + text.Recycle(); return @out; } public static int CalcWordWrapPositionA( ImFontPtr font, float scale, ImU8String text, float wrapWidth) { - fixed (byte* ptr = text.Span) + fixed (byte* ptr = text) { var r = (int)(ImGuiNative.CalcWordWrapPositionA(font.Handle, scale, ptr, ptr + text.Length, wrapWidth) - ptr); - text.Dispose(); + text.Recycle(); return r; } } @@ -131,9 +134,9 @@ public static unsafe partial class ImGui public static void InsertChars( ImGuiInputTextCallbackDataPtr self, int pos, ImU8String text) { - fixed (byte* ptr = text.Span) + fixed (byte* ptr = text) ImGuiNative.InsertChars(self.Handle, pos, ptr, ptr + text.Length); - text.Dispose(); + text.Recycle(); } public static void LabelText( @@ -143,8 +146,8 @@ public static unsafe partial class ImGui var window = ImGuiP.GetCurrentWindow().Handle; if (window->SkipItems != 0) { - label.Dispose(); - text.Dispose(); + label.Recycle(); + text.Recycle(); return; } @@ -165,8 +168,8 @@ public static unsafe partial class ImGui ImGuiP.ItemSize(totalBb, style.FramePadding.Y); if (!ImGuiP.ItemAdd(totalBb, 0)) { - label.Dispose(); - text.Dispose(); + label.Recycle(); + text.Recycle(); return; } @@ -179,8 +182,8 @@ public static unsafe partial class ImGui label.Span); } - label.Dispose(); - text.Dispose(); + label.Recycle(); + text.Recycle(); } public static void LogText(ImU8String text) @@ -190,7 +193,7 @@ public static unsafe partial class ImGui { g.LogBuffer.Buf.Resize(0); append(&g.Handle->LogBuffer, text.Span); - fixed (byte* textPtr = text.Span) + fixed (byte* textPtr = text) ImGuiPNative.ImFileWrite(textPtr, 1, (ulong)text.Length, g.LogFile); } else @@ -198,21 +201,24 @@ public static unsafe partial class ImGui append(&g.Handle->LogBuffer, text); } - text.Dispose(); + text.Recycle(); } - public static void PassFilter(ImGuiTextFilterPtr self, ImU8String text) + public static bool PassFilter(ImGuiTextFilterPtr self, ImU8String text) { - fixed (byte* textPtr = text.Span) - ImGuiNative.PassFilter(self.Handle, textPtr, textPtr + text.Length); - text.Dispose(); + fixed (byte* textPtr = text) + { + var r = ImGuiNative.PassFilter(self.Handle, textPtr, textPtr + text.Length) != 0; + text.Recycle(); + return r; + } } public static void RenderText( ImFontPtr self, ImDrawListPtr drawList, float size, Vector2 pos, uint col, Vector4 clipRect, ImU8String text, float wrapWidth = 0.0f, bool cpuFineClip = false) { - fixed (byte* textPtr = text.Span) + fixed (byte* textPtr = text) ImGuiNative.RenderText( self, drawList, @@ -224,7 +230,7 @@ public static unsafe partial class ImGui textPtr + text.Length, wrapWidth, cpuFineClip ? (byte)1 : (byte)0); - text.Dispose(); + text.Recycle(); } public static void SetTooltip(ImU8String text) @@ -232,14 +238,14 @@ public static unsafe partial class ImGui ImGuiP.BeginTooltipEx(ImGuiTooltipFlags.OverridePreviousTooltip, ImGuiWindowFlags.None); Text(text.Span); EndTooltip(); - text.Dispose(); + text.Recycle(); } public static void Text(ImU8String text) { - fixed (byte* ptr = text.Span) + fixed (byte* ptr = text) ImGuiNative.TextUnformatted(ptr, ptr + text.Length); - text.Dispose(); + text.Recycle(); } public static void TextColored(uint col, ImU8String text) @@ -247,7 +253,7 @@ public static unsafe partial class ImGui PushStyleColor(ImGuiCol.Text, col); Text(text.Span); PopStyleColor(); - text.Dispose(); + text.Recycle(); } public static void TextColored(scoped in Vector4 col, ImU8String text) @@ -255,19 +261,19 @@ public static unsafe partial class ImGui PushStyleColor(ImGuiCol.Text, col); Text(text.Span); PopStyleColor(); - text.Dispose(); + text.Recycle(); } public static void TextDisabled(ImU8String text) { TextColored(*GetStyleColorVec4(ImGuiCol.TextDisabled), text.Span); - text.Dispose(); + text.Recycle(); } public static void TextUnformatted(ImU8String text) { Text(text.Span); - text.Dispose(); + text.Recycle(); } public static void TextWrapped(ImU8String text) @@ -279,7 +285,7 @@ public static unsafe partial class ImGui Text(text.Span); if (needBackup) PopTextWrapPos(); - text.Dispose(); + text.Recycle(); } public static void TextColoredWrapped(uint col, ImU8String text) @@ -287,7 +293,7 @@ public static unsafe partial class ImGui PushStyleColor(ImGuiCol.Text, col); TextWrapped(text.Span); PopStyleColor(); - text.Dispose(); + text.Recycle(); } public static void TextColoredWrapped(scoped in Vector4 col, ImU8String text) @@ -295,7 +301,7 @@ public static unsafe partial class ImGui PushStyleColor(ImGuiCol.Text, col); TextWrapped(text.Span); PopStyleColor(); - text.Dispose(); + text.Recycle(); } public static bool TreeNode(ImU8String label) @@ -303,7 +309,7 @@ public static unsafe partial class ImGui var window = ImGuiP.GetCurrentWindow(); if (window.SkipItems) { - label.Dispose(); + label.Recycle(); return false; } @@ -311,7 +317,7 @@ public static unsafe partial class ImGui window.Handle->GetID(label.Span), ImGuiTreeNodeFlags.None, label.Span[..ImGuiP.FindRenderedTextEnd(label.Span, out _, out _)]); - label.Dispose(); + label.Recycle(); return res; } @@ -337,8 +343,8 @@ public static unsafe partial class ImGui res = ImGuiP.TreeNodeBehavior(window.Handle->GetID(id.Span), flags, label.Span[..label.Length]); } - id.Dispose(); - label.Dispose(); + id.Recycle(); + label.Recycle(); return res; } } diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Widgets.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Widgets.cs index 953df2908..889824f43 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Widgets.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGui.Widgets.cs @@ -6,13 +6,23 @@ namespace Dalamud.Bindings.ImGui; [SuppressMessage("ReSharper", "InconsistentNaming")] public static unsafe partial class ImGui { + public static bool ArrowButton(ImU8String strId, ImGuiDir dir) + { + fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) + { + var r = ImGuiNative.ArrowButton(strIdPtr, dir) != 0; + strId.Recycle(); + return r; + } + } + public static bool Begin(ImU8String name, ref bool open, ImGuiWindowFlags flags = ImGuiWindowFlags.None) { fixed (byte* namePtr = &name.GetPinnableNullTerminatedReference()) fixed (bool* openPtr = &open) { var r = ImGuiNative.Begin(namePtr, openPtr, flags) != 0; - name.Dispose(); + name.Recycle(); return r; } } @@ -22,32 +32,35 @@ public static unsafe partial class ImGui fixed (byte* namePtr = &name.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.Begin(namePtr, null, flags) != 0; - name.Dispose(); + name.Recycle(); return r; } } - public static bool BeginChild(ImU8String strId, Vector2 size = default, bool border = false, ImGuiWindowFlags flags = ImGuiWindowFlags.None) + public static bool BeginChild( + ImU8String strId, Vector2 size = default, bool border = false, ImGuiWindowFlags flags = ImGuiWindowFlags.None) { fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.BeginChild(strIdPtr, size, border ? (byte)1 : (byte)0, flags) != 0; - strId.Dispose(); + strId.Recycle(); return r; } } - public static bool BeginChild(uint id, Vector2 size = default, bool border = false, ImGuiWindowFlags flags = ImGuiWindowFlags.None) => + public static bool BeginChild( + uint id, Vector2 size = default, bool border = false, ImGuiWindowFlags flags = ImGuiWindowFlags.None) => ImGuiNative.BeginChild(id, size, border ? (byte)1 : (byte)0, flags) != 0; - public static bool BeginCombo(ImU8String label, ImU8String previewValue, ImGuiComboFlags flags = ImGuiComboFlags.None) + public static bool BeginCombo( + ImU8String label, ImU8String previewValue, ImGuiComboFlags flags = ImGuiComboFlags.None) { fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) fixed (byte* previewValuePtr = &previewValue.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.BeginCombo(labelPtr, previewValuePtr, flags) != 0; - label.Dispose(); - previewValue.Dispose(); + label.Recycle(); + previewValue.Recycle(); return r; } } @@ -57,7 +70,7 @@ public static unsafe partial class ImGui fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.BeginListBox(labelPtr, size) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -67,7 +80,7 @@ public static unsafe partial class ImGui fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.BeginMenu(labelPtr, enabled ? (byte)1 : (byte)0) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -77,37 +90,40 @@ public static unsafe partial class ImGui fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.BeginPopup(strIdPtr, flags) != 0; - strId.Dispose(); + strId.Recycle(); return r; } } - public static bool BeginPopupContextItem(ImU8String strId, ImGuiPopupFlags popupFlags = ImGuiPopupFlags.MouseButtonDefault) + public static bool BeginPopupContextItem( + ImU8String strId, ImGuiPopupFlags popupFlags = ImGuiPopupFlags.MouseButtonDefault) { fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.BeginPopupContextItem(strIdPtr, popupFlags) != 0; - strId.Dispose(); + strId.Recycle(); return r; } } - public static bool BeginPopupContextWindow(ImU8String strId, ImGuiPopupFlags popupFlags = ImGuiPopupFlags.MouseButtonDefault) + public static bool BeginPopupContextWindow( + ImU8String strId, ImGuiPopupFlags popupFlags = ImGuiPopupFlags.MouseButtonDefault) { fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.BeginPopupContextWindow(strIdPtr, popupFlags) != 0; - strId.Dispose(); + strId.Recycle(); return r; } } - public static bool BeginPopupContextVoid(ImU8String strId, ImGuiPopupFlags popupFlags = ImGuiPopupFlags.MouseButtonDefault) + public static bool BeginPopupContextVoid( + ImU8String strId, ImGuiPopupFlags popupFlags = ImGuiPopupFlags.MouseButtonDefault) { fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.BeginPopupContextVoid(strIdPtr, popupFlags) != 0; - strId.Dispose(); + strId.Recycle(); return r; } } @@ -119,7 +135,7 @@ public static unsafe partial class ImGui fixed (bool* openPtr = &open) { var r = ImGuiNative.BeginPopupModal(namePtr, openPtr, flags) != 0; - name.Dispose(); + name.Recycle(); return r; } } @@ -129,7 +145,7 @@ public static unsafe partial class ImGui fixed (byte* namePtr = &name.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.BeginPopupModal(namePtr, null, flags) != 0; - name.Dispose(); + name.Recycle(); return r; } } @@ -139,7 +155,7 @@ public static unsafe partial class ImGui fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.BeginTabBar(strIdPtr, flags) != 0; - strId.Dispose(); + strId.Recycle(); return r; } } @@ -151,7 +167,7 @@ public static unsafe partial class ImGui fixed (bool* pOpenPtr = &pOpen) { var r = ImGuiNative.BeginTabItem(labelPtr, pOpenPtr, flags) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -161,7 +177,7 @@ public static unsafe partial class ImGui fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.BeginTabItem(labelPtr, null, flags) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -173,7 +189,7 @@ public static unsafe partial class ImGui fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.BeginTable(strIdPtr, column, flags, outerSize, innerWidth) != 0; - strId.Dispose(); + strId.Recycle(); return r; } } @@ -183,7 +199,7 @@ public static unsafe partial class ImGui fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.Button(labelPtr, size) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -194,7 +210,7 @@ public static unsafe partial class ImGui fixed (bool* vPtr = &v) { var r = ImGuiNative.Checkbox(labelPtr, vPtr) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -228,7 +244,7 @@ public static unsafe partial class ImGui flags &= ~flagsValue; } - label.Dispose(); + label.Recycle(); return pressed; } } @@ -240,7 +256,7 @@ public static unsafe partial class ImGui fixed (bool* visiblePtr = &visible) { var r = ImGuiNative.CollapsingHeader(labelPtr, visiblePtr, flags) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -250,7 +266,7 @@ public static unsafe partial class ImGui fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.CollapsingHeader(labelPtr, null, flags) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -262,7 +278,7 @@ public static unsafe partial class ImGui fixed (byte* descIdPtr = &descId.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.ColorButton(descIdPtr, col, flags, size) != 0; - descId.Dispose(); + descId.Recycle(); return r; } } @@ -271,7 +287,7 @@ public static unsafe partial class ImGui { fixed (byte* idPtr = &id.GetPinnableNullTerminatedReference()) ImGuiNative.Columns(count, idPtr, border ? (byte)1 : (byte)0); - id.Dispose(); + id.Recycle(); } public static bool DebugCheckVersionAndDataLayout( @@ -280,8 +296,15 @@ public static unsafe partial class ImGui { fixed (byte* versionPtr = &versionStr.GetPinnableNullTerminatedReference()) { - var r = ImGuiNative.DebugCheckVersionAndDataLayout(versionPtr, szIo, szStyle, szVec2, szVec4, szDrawVert, szDrawIdx) != 0; - versionStr.Dispose(); + var r = ImGuiNative.DebugCheckVersionAndDataLayout( + versionPtr, + szIo, + szStyle, + szVec2, + szVec4, + szDrawVert, + szDrawIdx) != 0; + versionStr.Recycle(); return r; } } @@ -291,7 +314,7 @@ public static unsafe partial class ImGui fixed (byte* textPtr = &text.GetPinnableNullTerminatedReference()) { ImGuiNative.DebugTextEncoding(textPtr); - text.Dispose(); + text.Recycle(); } } @@ -300,7 +323,7 @@ public static unsafe partial class ImGui fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference("Filter (inc,-exc)"u8)) { var r = ImGuiNative.Draw(self.Handle, labelPtr, width) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -310,7 +333,7 @@ public static unsafe partial class ImGui fixed (byte* defaultFilterPtr = &defaultFilter.GetPinnableNullTerminatedReference("\0"u8)) { var r = ImGuiNative.ImGuiTextFilter(defaultFilterPtr); - defaultFilter.Dispose(); + defaultFilter.Recycle(); return r; } } @@ -329,7 +352,7 @@ public static unsafe partial class ImGui fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.InvisibleButton(strIdPtr, size, flags) != 0; - strId.Dispose(); + strId.Recycle(); return r; } } @@ -339,7 +362,7 @@ public static unsafe partial class ImGui fixed (byte* typePtr = &type.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.IsDataType(self.Handle, typePtr) != 0; - type.Dispose(); + type.Recycle(); return r; } } @@ -349,7 +372,7 @@ public static unsafe partial class ImGui fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.IsPopupOpen(strIdPtr, flags) != 0; - strId.Dispose(); + strId.Recycle(); return r; } } @@ -358,21 +381,21 @@ public static unsafe partial class ImGui { fixed (byte* iniFilenamePtr = &iniFilename.GetPinnableNullTerminatedReference()) ImGuiNative.LoadIniSettingsFromDisk(iniFilenamePtr); - iniFilename.Dispose(); + iniFilename.Recycle(); } public static void LoadIniSettingsFromMemory(ImU8String iniData) { - fixed (byte* iniDataPtr = iniData.Span) + fixed (byte* iniDataPtr = iniData) ImGuiNative.LoadIniSettingsFromMemory(iniDataPtr, (nuint)iniData.Length); - iniData.Dispose(); + iniData.Recycle(); } public static void LogToFile(int autoOpenDepth = -1, ImU8String filename = default) { fixed (byte* filenamePtr = &filename.GetPinnableNullTerminatedReference()) ImGuiNative.LogToFile(autoOpenDepth, filenamePtr); - filename.Dispose(); + filename.Recycle(); } public static bool MenuItem( @@ -381,9 +404,13 @@ public static unsafe partial class ImGui fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) fixed (byte* shortcutPtr = &shortcut.GetPinnableNullTerminatedReference()) { - var r = ImGuiNative.MenuItem(labelPtr, shortcutPtr, selected ? (byte)1 : (byte)0, enabled ? (byte)1 : (byte)0) != 0; - label.Dispose(); - shortcut.Dispose(); + var r = ImGuiNative.MenuItem( + labelPtr, + shortcutPtr, + selected ? (byte)1 : (byte)0, + enabled ? (byte)1 : (byte)0) != 0; + label.Recycle(); + shortcut.Recycle(); return r; } } @@ -395,8 +422,8 @@ public static unsafe partial class ImGui fixed (bool* selectedPtr = &selected) { var r = ImGuiNative.MenuItem(labelPtr, shortcutPtr, selectedPtr, enabled ? (byte)1 : (byte)0) != 0; - label.Dispose(); - shortcut.Dispose(); + label.Recycle(); + shortcut.Recycle(); return r; } } @@ -407,7 +434,7 @@ public static unsafe partial class ImGui fixed (bool* selectedPtr = &selected) { var r = ImGuiNative.MenuItem(labelPtr, null, selectedPtr, enabled ? (byte)1 : (byte)0) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -416,8 +443,12 @@ public static unsafe partial class ImGui { fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) { - var r = ImGuiNative.MenuItem(labelPtr, null, selected ? (byte)1 : (byte)0, enabled ? (byte)1 : (byte)0) != 0; - label.Dispose(); + var r = ImGuiNative.MenuItem( + labelPtr, + null, + selected ? (byte)1 : (byte)0, + enabled ? (byte)1 : (byte)0) != 0; + label.Recycle(); return r; } } @@ -426,39 +457,40 @@ public static unsafe partial class ImGui { fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) ImGuiNative.OpenPopup(strIdPtr, popupFlags); - strId.Dispose(); + strId.Recycle(); } - public static void OpenPopup(uint id, ImGuiPopupFlags popupFlags = ImGuiPopupFlags.None) => ImGuiNative.OpenPopup(id, popupFlags); + public static void OpenPopup(uint id, ImGuiPopupFlags popupFlags = ImGuiPopupFlags.None) => + ImGuiNative.OpenPopup(id, popupFlags); public static void OpenPopupOnItemClick( ImU8String strId, ImGuiPopupFlags popupFlags = ImGuiPopupFlags.MouseButtonDefault) { fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) ImGuiNative.OpenPopupOnItemClick(strIdPtr, popupFlags); - strId.Dispose(); + strId.Recycle(); } public static void ProgressBar(float fraction, Vector2 sizeArg, ImU8String overlay = default) { fixed (byte* overlayPtr = &overlay.GetPinnableNullTerminatedReference()) ImGuiNative.ProgressBar(fraction, sizeArg, overlayPtr); - overlay.Dispose(); + overlay.Recycle(); } public static void ProgressBar(float fraction, ImU8String overlay = default) { fixed (byte* overlayPtr = &overlay.GetPinnableNullTerminatedReference()) ImGuiNative.ProgressBar(fraction, new(-float.MinValue, 0), overlayPtr); - overlay.Dispose(); + overlay.Recycle(); } public static bool RadioButton(ImU8String label, bool active) { fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) { - var r = ImGuiNative.RadioButton(labelPtr, active ? (byte)1:(byte)0) != 0; - label.Dispose(); + var r = ImGuiNative.RadioButton(labelPtr, active ? (byte)1 : (byte)0) != 0; + label.Recycle(); return r; } } @@ -468,8 +500,8 @@ public static unsafe partial class ImGui fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) { var pressed = ImGuiNative.RadioButton( - labelPtr, - EqualityComparer.Default.Equals(v, vButton) ? (byte)1 : (byte)0) != 0; + labelPtr, + EqualityComparer.Default.Equals(v, vButton) ? (byte)1 : (byte)0) != 0; if (pressed) v = vButton; return pressed; @@ -483,23 +515,26 @@ public static unsafe partial class ImGui } public static bool Selectable( - ImU8String label, bool selected = false, ImGuiSelectableFlags flags = ImGuiSelectableFlags.None, Vector2 size = default) + ImU8String label, bool selected = false, ImGuiSelectableFlags flags = ImGuiSelectableFlags.None, + Vector2 size = default) { fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) { - var r = ImGuiNative.Selectable(labelPtr, selected ?(byte)1:(byte)0, flags, size)!=0; - label.Dispose(); + var r = ImGuiNative.Selectable(labelPtr, selected ? (byte)1 : (byte)0, flags, size) != 0; + label.Recycle(); return r; } } - public static bool Selectable(ImU8String label, ref bool selected, ImGuiSelectableFlags flags = ImGuiSelectableFlags.None, Vector2 size = default) + public static bool Selectable( + ImU8String label, ref bool selected, ImGuiSelectableFlags flags = ImGuiSelectableFlags.None, + Vector2 size = default) { fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) - fixed (bool* selectedPtr = &selected) + fixed (bool* selectedPtr = &selected) { - var r = ImGuiNative.Selectable(labelPtr, selectedPtr, flags, size)!=0; - label.Dispose(); + var r = ImGuiNative.Selectable(labelPtr, selectedPtr, flags, size) != 0; + label.Recycle(); return r; } } @@ -508,16 +543,16 @@ public static unsafe partial class ImGui { fixed (byte* textPtr = &text.GetPinnableNullTerminatedReference()) ImGuiNative.SetClipboardText(textPtr); - text.Dispose(); + text.Recycle(); } - public static bool SetDragDropPayload(ImU8String type, ReadOnlySpan data, ImGuiCond cond) + public static bool SetDragDropPayload(ImU8String type, ReadOnlySpan data, ImGuiCond cond = ImGuiCond.None) { fixed (byte* typePtr = &type.GetPinnableNullTerminatedReference()) fixed (byte* dataPtr = data) { - var r = ImGuiNative.SetDragDropPayload(typePtr, dataPtr, (nuint)data.Length, cond)!=0; - type.Dispose(); + var r = ImGuiNative.SetDragDropPayload(typePtr, dataPtr, (nuint)data.Length, cond) != 0; + type.Recycle(); return r; } } @@ -526,50 +561,60 @@ public static unsafe partial class ImGui { fixed (byte* tabItemPtr = &tabOrDockedWindowLabel.GetPinnableNullTerminatedReference()) ImGuiNative.SetTabItemClosed(tabItemPtr); - tabOrDockedWindowLabel.Dispose(); + tabOrDockedWindowLabel.Recycle(); } - public static void SetWindowCollapsed(bool collapsed, ImGuiCond cond = ImGuiCond.None) => ImGuiNative.SetWindowCollapsed(collapsed ? (byte)1 : (byte)0, cond); + public static void SetWindowCollapsed(bool collapsed, ImGuiCond cond = ImGuiCond.None) => + ImGuiNative.SetWindowCollapsed(collapsed ? (byte)1 : (byte)0, cond); public static void SetWindowCollapsed(ImU8String name, bool collapsed, ImGuiCond cond = ImGuiCond.None) { fixed (byte* namePtr = &name.GetPinnableNullTerminatedReference()) ImGuiNative.SetWindowCollapsed(namePtr, collapsed ? (byte)1 : (byte)0, cond); - name.Dispose(); + name.Recycle(); } + ///

Sets the current window to be focused / top-most. + /// Prefer using . public static void SetWindowFocus() => ImGuiNative.SetWindowFocus(); + /// Sets a named window to be focused / top-most. + /// Name of the window to focus. Use default to remove focus. public static void SetWindowFocus(ImU8String name) { fixed (byte* namePtr = &name.GetPinnableNullTerminatedReference()) ImGuiNative.SetWindowFocus(namePtr); - name.Dispose(); + name.Recycle(); } - public static void SetWindowPos(Vector2 pos, ImGuiCond cond = ImGuiCond.None) => ImGuiNative.SetWindowPos(pos, cond); + /// Removes focus from any window. + public static void ClearWindowFocus() => ImGuiNative.SetWindowFocus(null); + + public static void SetWindowPos(Vector2 pos, ImGuiCond cond = ImGuiCond.None) => + ImGuiNative.SetWindowPos(pos, cond); public static void SetWindowPos(ImU8String name, Vector2 pos, ImGuiCond cond = ImGuiCond.None) { fixed (byte* namePtr = &name.GetPinnableNullTerminatedReference()) ImGuiNative.SetWindowPos(namePtr, pos, cond); - name.Dispose(); + name.Recycle(); } - public static void SetWindowSize(Vector2 size, ImGuiCond cond = ImGuiCond.None) => ImGuiNative.SetWindowSize(size, cond); + public static void SetWindowSize(Vector2 size, ImGuiCond cond = ImGuiCond.None) => + ImGuiNative.SetWindowSize(size, cond); public static void SetWindowSize(ImU8String name, Vector2 size, ImGuiCond cond = ImGuiCond.None) { fixed (byte* namePtr = &name.GetPinnableNullTerminatedReference()) ImGuiNative.SetWindowSize(namePtr, size, cond); - name.Dispose(); + name.Recycle(); } public static void ShowFontSelector(ImU8String label) { fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) ImGuiNative.ShowFontSelector(labelPtr); - label.Dispose(); + label.Recycle(); } public static bool ShowStyleSelector(ImU8String label) @@ -577,7 +622,7 @@ public static unsafe partial class ImGui fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.ShowStyleSelector(labelPtr) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -587,7 +632,7 @@ public static unsafe partial class ImGui fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.SmallButton(labelPtr) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -597,7 +642,7 @@ public static unsafe partial class ImGui fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) { var r = ImGuiNative.TabItemButton(labelPtr, flags) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -606,7 +651,7 @@ public static unsafe partial class ImGui { fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) ImGuiNative.TableHeader(labelPtr); - label.Dispose(); + label.Recycle(); } public static void TableSetupColumn( @@ -615,14 +660,14 @@ public static unsafe partial class ImGui { fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) ImGuiNative.TableSetupColumn(labelPtr, flags, initWidthOrWeight, userId); - label.Dispose(); + label.Recycle(); } public static void TreePush(ImU8String strId) { fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) ImGuiNative.TreePush(strIdPtr); - strId.Dispose(); + strId.Recycle(); } public static void TreePush(nint ptrId) => ImGuiNative.TreePush((void*)ptrId); @@ -632,10 +677,10 @@ public static unsafe partial class ImGui { prefix.AppendLiteral(": "); prefix.AppendFormatted(value); - fixed (byte* prefixPtr = prefix.Span) + fixed (byte* prefixPtr = prefix) { ImGuiNative.TextUnformatted(prefixPtr, prefixPtr + prefix.Length); - prefix.Dispose(); + prefix.Recycle(); } } @@ -647,8 +692,8 @@ public static unsafe partial class ImGui fixed (byte* floatPtr = &floatFormat.GetPinnableNullTerminatedReference()) { ImGuiNative.Value(prefixPtr, value, floatPtr); - prefix.Dispose(); - floatFormat.Dispose(); + prefix.Recycle(); + floatFormat.Recycle(); } } } diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiInputTextCallbackData.Custom.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiInputTextCallbackData.Custom.cs index 9d910e6d7..ee90e3b82 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiInputTextCallbackData.Custom.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiInputTextCallbackData.Custom.cs @@ -3,6 +3,7 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiInputTextCallbackData { public readonly Span BufSpan => new(this.Buf, this.BufSize); + public readonly Span BufTextSpan => new(this.Buf, this.BufTextLen); public void InsertChars(int pos, ImU8String text) @@ -15,6 +16,7 @@ public unsafe partial struct ImGuiInputTextCallbackData public unsafe partial struct ImGuiInputTextCallbackDataPtr { public readonly Span BufSpan => this.Handle->BufSpan; + public readonly Span BufTextSpan => this.Handle->BufTextSpan; public void InsertChars(int pos, ImU8String text) => ImGui.InsertChars(this, pos, text); diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiNative.Custom.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiNative.Custom.cs index 66626ee3b..62d351993 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiNative.Custom.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiNative.Custom.cs @@ -28,7 +28,7 @@ public static unsafe partial class ImGuiNative [LibraryImport(LibraryName, EntryPoint = "igInputTextEx")] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - public static partial int InputTextEx( + public static partial byte InputTextEx( byte* label, byte* hint, byte* buf, diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiP.Misc.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiP.Misc.cs index 8e61f8f07..3947d18d2 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiP.Misc.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiP.Misc.cs @@ -12,7 +12,7 @@ public unsafe partial class ImGuiP fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.ArrowButtonEx(strIdPtr, dir, sizeArg, flags) != 0; - strId.Dispose(); + strId.Recycle(); return r; } } @@ -22,7 +22,7 @@ public unsafe partial class ImGuiP fixed (byte* namePtr = &name.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.BeginChildEx(namePtr, id, sizeArg, border ? (byte)1 : (byte)0, flags) != 0; - name.Dispose(); + name.Recycle(); return r; } } @@ -31,7 +31,7 @@ public unsafe partial class ImGuiP { fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) ImGuiPNative.BeginColumns(strIdPtr, count, flags); - strId.Dispose(); + strId.Recycle(); } public static bool BeginMenuEx(ImU8String label, ImU8String icon = default, bool enabled = true) @@ -40,8 +40,8 @@ public unsafe partial class ImGuiP fixed (byte* iconPtr = &icon.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.BeginMenuEx(labelPtr, iconPtr, enabled ? (byte)1 : (byte)0) != 0; - label.Dispose(); - icon.Dispose(); + label.Recycle(); + icon.Recycle(); return r; } } @@ -53,7 +53,7 @@ public unsafe partial class ImGuiP fixed (byte* namePtr = &name.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.BeginTableEx(namePtr, id, columnsCount, flags, outerSize, innerWidth) != 0; - name.Dispose(); + name.Recycle(); return r; } } @@ -64,7 +64,7 @@ public unsafe partial class ImGuiP fixed (byte* namePtr = &name.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.BeginViewportSideBar(namePtr, viewport, dir, size, windowFlags) != 0; - name.Dispose(); + name.Recycle(); return r; } } @@ -75,7 +75,7 @@ public unsafe partial class ImGuiP fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.ButtonEx(labelPtr, sizeArg, flags) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -97,7 +97,7 @@ public unsafe partial class ImGuiP fixed (byte* textPtr = &text.GetPinnableNullTerminatedReference()) fixed (float* colPtr = col) ImGuiPNative.ColorTooltip(textPtr, colPtr, flags); - text.Dispose(); + text.Recycle(); } public static ImGuiWindowSettingsPtr CreateNewWindowSettings(ImU8String name) @@ -105,7 +105,7 @@ public unsafe partial class ImGuiP fixed (byte* namePtr = &name.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.CreateNewWindowSettings(namePtr); - name.Dispose(); + name.Recycle(); return r; } } @@ -123,8 +123,8 @@ public unsafe partial class ImGuiP fixed (byte* formatPtr = &format.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.DataTypeApplyFromText(bufPtr, dataType, &data, formatPtr) != 0; - format.Dispose(); - buf.Dispose(); + format.Recycle(); + buf.Recycle(); return r; } } @@ -133,7 +133,7 @@ public unsafe partial class ImGuiP { fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) ImGuiPNative.DebugNodeDockNode(node, labelPtr); - label.Dispose(); + label.Recycle(); } public static void DebugNodeDrawList( @@ -141,28 +141,28 @@ public unsafe partial class ImGuiP { fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) ImGuiPNative.DebugNodeDrawList(window, viewport, drawList, labelPtr); - label.Dispose(); + label.Recycle(); } public static void DebugNodeStorage(ImGuiStoragePtr storage, ImU8String label) { fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) ImGuiPNative.DebugNodeStorage(storage, labelPtr); - label.Dispose(); + label.Recycle(); } public static void DebugNodeTabBar(ImGuiTabBarPtr tabBar, ImU8String label) { fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) ImGuiPNative.DebugNodeTabBar(tabBar, labelPtr); - label.Dispose(); + label.Recycle(); } public static void DebugNodeWindow(ImGuiWindowPtr window, ImU8String label) { fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) ImGuiPNative.DebugNodeWindow(window, labelPtr); - label.Dispose(); + label.Recycle(); } public static void DebugNodeWindowsList(scoped in ImVector windows, ImU8String label) @@ -170,7 +170,7 @@ public unsafe partial class ImGuiP fixed (ImVector* windowsPtr = &windows) fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) ImGuiPNative.DebugNodeWindowsList(windowsPtr, labelPtr); - label.Dispose(); + label.Recycle(); } public static void DockBuilderCopyWindowSettings(ImU8String srcName, ImU8String dstName) @@ -178,15 +178,15 @@ public unsafe partial class ImGuiP fixed (byte* srcNamePtr = &srcName.GetPinnableNullTerminatedReference()) fixed (byte* dstNamePtr = &dstName.GetPinnableNullTerminatedReference()) ImGuiPNative.DockBuilderCopyWindowSettings(srcNamePtr, dstNamePtr); - srcName.Dispose(); - dstName.Dispose(); + srcName.Recycle(); + dstName.Recycle(); } public static void DockBuilderDockWindow(ImU8String windowName, uint nodeId) { fixed (byte* windowNamePtr = &windowName.GetPinnableNullTerminatedReference()) ImGuiPNative.DockBuilderDockWindow(windowNamePtr, nodeId); - windowName.Dispose(); + windowName.Recycle(); } public static bool DragBehavior( @@ -196,7 +196,7 @@ public unsafe partial class ImGuiP fixed (byte* formatPtr = &format.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.DragBehavior(id, dataType, pV, vSpeed, pMin, pMax, formatPtr, flags) != 0; - format.Dispose(); + format.Recycle(); return r; } } @@ -206,7 +206,7 @@ public unsafe partial class ImGuiP fixed (byte* namePtr = &name.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.FindOrCreateWindowSettings(namePtr); - name.Dispose(); + name.Recycle(); return r; } } @@ -216,7 +216,7 @@ public unsafe partial class ImGuiP fixed (byte* typeNamePtr = &typeName.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.FindSettingsHandler(typeNamePtr); - typeName.Dispose(); + typeName.Recycle(); return r; } } @@ -226,7 +226,7 @@ public unsafe partial class ImGuiP fixed (byte* namePtr = &name.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.FindWindowByName(namePtr); - name.Dispose(); + name.Recycle(); return r; } } @@ -236,7 +236,7 @@ public unsafe partial class ImGuiP fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.GetColumnsID(strIdPtr, count); - strId.Dispose(); + strId.Recycle(); return r; } } @@ -246,7 +246,7 @@ public unsafe partial class ImGuiP fixed (byte* strIdPtr = &strId.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.GetIDWithSeed(strIdPtr, strIdPtr + strId.Length, seed); - strId.Dispose(); + strId.Recycle(); return r; } } @@ -259,8 +259,8 @@ public unsafe partial class ImGuiP fixed (nuint* outFileSizePtr = &outFileSize) { var r = ImGuiPNative.ImFileLoadToMemory(filenamePtr, modePtr, outFileSizePtr, paddingBytes); - filename.Dispose(); - mode.Dispose(); + filename.Recycle(); + mode.Recycle(); return r; } } @@ -271,8 +271,8 @@ public unsafe partial class ImGuiP fixed (byte* modePtr = &mode.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.ImFileLoadToMemory(filenamePtr, modePtr, null, paddingBytes); - filename.Dispose(); - mode.Dispose(); + filename.Recycle(); + mode.Recycle(); return r; } } @@ -283,8 +283,8 @@ public unsafe partial class ImGuiP fixed (byte* modePtr = &mode.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.ImFileOpen(filenamePtr, modePtr); - filename.Dispose(); - mode.Dispose(); + filename.Recycle(); + mode.Recycle(); return r; } } @@ -339,7 +339,7 @@ public unsafe partial class ImGuiP { fixed (byte* fmtPtr = &fmt.GetPinnableNullTerminatedReference()) ImGuiPNative.ImFormatStringToTempBuffer(outBuf, outBufEnd, fmtPtr); - fmt.Dispose(); + fmt.Recycle(); } public static ImGuiWindowPtr ImGuiWindow(ImGuiContextPtr context, ImU8String name) @@ -347,7 +347,7 @@ public unsafe partial class ImGuiP fixed (byte* namePtr = &name.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.ImGuiWindow(context, namePtr); - name.Dispose(); + name.Recycle(); return r; } } @@ -386,9 +386,9 @@ public unsafe partial class ImGuiP shortcutPtr, selected ? (byte)1 : (byte)0, enabled ? (byte)1 : (byte)0) != 0; - label.Dispose(); - icon.Dispose(); - shortcut.Dispose(); + label.Recycle(); + icon.Recycle(); + shortcut.Recycle(); return r; } } @@ -397,7 +397,7 @@ public unsafe partial class ImGuiP { fixed (byte* typeNamePtr = &typeName.GetPinnableNullTerminatedReference()) ImGuiPNative.RemoveSettingsHandler(typeNamePtr); - typeName.Dispose(); + typeName.Recycle(); } public static bool SliderBehavior( @@ -418,7 +418,7 @@ public unsafe partial class ImGuiP formatPtr, flags, outGrabBb) != 0; - format.Dispose(); + format.Recycle(); return r; } } @@ -440,7 +440,7 @@ public unsafe partial class ImGuiP fixed (bool* openPtr = &open) { var r = ImGuiPNative.TabItemEx(tabBar, labelPtr, openPtr, flags, dockedWindow) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -466,7 +466,7 @@ public unsafe partial class ImGuiP textClippedPtr); } - label.Dispose(); + label.Recycle(); } public static bool TempInputScalar( @@ -479,7 +479,7 @@ public unsafe partial class ImGuiP fixed (T* dataPtr = &data) { var r = ImGuiPNative.TempInputScalar(bb, id, labelPtr, dataType, dataPtr, formatPtr, &min, &max) != 0; - label.Dispose(); + label.Recycle(); return r; } } @@ -489,7 +489,7 @@ public unsafe partial class ImGuiP fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) { var r = ImGuiPNative.TreeNodeBehavior(id, flags, labelPtr, labelPtr + label.Length) != 0; - label.Dispose(); + label.Recycle(); return r; } } diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiP.Plot.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiP.Plot.cs index 8f552d4fb..aab161b9c 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiP.Plot.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiP.Plot.cs @@ -27,8 +27,8 @@ public static unsafe partial class ImGuiP scaleMin, scaleMax, frameSize); - label.Dispose(); - overlayText.Dispose(); + label.Recycle(); + overlayText.Recycle(); return r; } } @@ -59,8 +59,8 @@ public static unsafe partial class ImGuiP scaleMin, scaleMax, frameSize); - label.Dispose(); - overlayText.Dispose(); + label.Recycle(); + overlayText.Recycle(); return r; } } @@ -69,7 +69,7 @@ public static unsafe partial class ImGuiP ImGuiPlotType plotType, ImU8String label, ImGui.GetFloatRefContextDelegate valuesGetter, scoped in TContext context, int valuesCount, int valuesOffset, ImU8String overlayText, float scaleMin, float scaleMax, Vector2 frameSize) - where TContext: allows ref struct + where TContext : allows ref struct { fixed (byte* labelPtr = &label.GetPinnableNullTerminatedReference()) fixed (byte* overlayTextPtr = &overlayText.GetPinnableNullTerminatedReference()) @@ -91,8 +91,8 @@ public static unsafe partial class ImGuiP scaleMin, scaleMax, frameSize); - label.Dispose(); - overlayText.Dispose(); + label.Recycle(); + overlayText.Recycle(); return r; } } diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiP.Text.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiP.Text.cs index 2f12a55c1..c4a2a48a9 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiP.Text.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiP.Text.cs @@ -13,7 +13,7 @@ public static unsafe partial class ImGuiP ImGui.append(&g->DebugLogBuf, text.Span); if ((g->DebugLogFlags & ImGuiDebugLogFlags.OutputToTty) != ImGuiDebugLogFlags.None) Debug.Write(text.ToString()); - text.Dispose(); + text.Recycle(); } public static int FindRenderedTextEnd( @@ -35,20 +35,20 @@ public static unsafe partial class ImGuiP FindRenderedTextEnd(textBuf.Span, out var beforeBytes, out var afterBytes); before = text[..Encoding.UTF8.GetCharCount(beforeBytes)]; after = text[before.Length..]; - textBuf.Dispose(); + textBuf.Recycle(); return before.Length; } public static uint GetID(ImGuiWindowPtr self, ImU8String str) { - fixed (byte* strPtr = str.Span) + fixed (byte* strPtr = str) { var seed = *self.IDStack.Back; var id = ImGuiPNative.ImHashStr(strPtr, (nuint)str.Length, seed); var g = ImGui.GetCurrentContext(); if (g.DebugHookIdInfo == id) DebugHookIdInfo(id, (ImGuiDataType)ImGuiDataTypePrivate.String, strPtr, strPtr + str.Length); - str.Dispose(); + str.Recycle(); return id; } } @@ -63,10 +63,10 @@ public static unsafe partial class ImGuiP public static uint ImHashStr(ImU8String data, uint seed = 0) { - fixed (byte* ptr = data.Span) + fixed (byte* ptr = data) { var res = ImGuiPNative.ImHashStr(ptr, (nuint)data.Length, seed); - data.Dispose(); + data.Recycle(); return res; } } @@ -133,24 +133,24 @@ public static unsafe partial class ImGuiP public static void LogRenderedText(scoped in Vector2 refPos, ImU8String text) { fixed (Vector2* refPosPtr = &refPos) - fixed (byte* textPtr = text.Span) + fixed (byte* textPtr = text) ImGuiPNative.LogRenderedText(refPosPtr, textPtr, textPtr + text.Length); - text.Dispose(); + text.Recycle(); } public static void RenderText(Vector2 pos, ImU8String text, bool hideTextAfterHash = true) { - fixed (byte* textPtr = text.Span) + fixed (byte* textPtr = text) ImGuiPNative.RenderText(pos, textPtr, textPtr + text.Length, hideTextAfterHash ? (byte)1 : (byte)0); - text.Dispose(); + text.Recycle(); } public static void RenderTextWrapped( Vector2 pos, ImU8String text, float wrapWidth) { - fixed (byte* textPtr = text.Span) + fixed (byte* textPtr = text) ImGuiPNative.RenderTextWrapped(pos, textPtr, textPtr + text.Length, wrapWidth); - text.Dispose(); + text.Recycle(); } public static void RenderTextClipped( @@ -160,7 +160,7 @@ public static unsafe partial class ImGuiP { var textSizeIfKnownOrDefault = textSizeIfKnown ?? default; var clipRectOrDefault = clipRect ?? default; - fixed (byte* textPtr = text.Span) + fixed (byte* textPtr = text) ImGuiPNative.RenderTextClipped( posMin, posMax, @@ -169,7 +169,7 @@ public static unsafe partial class ImGuiP textSizeIfKnown.HasValue ? &textSizeIfKnownOrDefault : null, align, clipRect.HasValue ? &clipRectOrDefault : null); - text.Dispose(); + text.Recycle(); } public static void RenderTextClippedEx( @@ -179,7 +179,7 @@ public static unsafe partial class ImGuiP { var textSizeIfKnownOrDefault = textSizeIfKnown ?? default; var clipRectOrDefault = clipRect ?? default; - fixed (byte* textPtr = text.Span) + fixed (byte* textPtr = text) ImGuiPNative.RenderTextClippedEx( drawList.Handle, posMin, @@ -189,7 +189,7 @@ public static unsafe partial class ImGuiP textSizeIfKnown.HasValue ? &textSizeIfKnownOrDefault : null, align, clipRect.HasValue ? &clipRectOrDefault : null); - text.Dispose(); + text.Recycle(); } public static void RenderTextEllipsis( @@ -197,7 +197,7 @@ public static unsafe partial class ImGuiP ImU8String text, scoped in Vector2? textSizeIfKnown = null) { var textSizeIfKnownOrDefault = textSizeIfKnown ?? default; - fixed (byte* textPtr = text.Span) + fixed (byte* textPtr = text) ImGuiPNative.RenderTextEllipsis( drawList.Handle, posMin, @@ -207,7 +207,7 @@ public static unsafe partial class ImGuiP textPtr, textPtr + text.Length, textSizeIfKnown.HasValue ? &textSizeIfKnownOrDefault : null); - text.Dispose(); + text.Recycle(); } public static void TextEx(ReadOnlySpan text, ImGuiTextFlags flags) diff --git a/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiTextFilter.Custom.cs b/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiTextFilter.Custom.cs index 03795d5e7..4ef7042eb 100644 --- a/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiTextFilter.Custom.cs +++ b/imgui/Dalamud.Bindings.ImGui/Custom/ImGuiTextFilter.Custom.cs @@ -2,21 +2,21 @@ namespace Dalamud.Bindings.ImGui; public unsafe partial struct ImGuiTextFilter { - public void Draw(ImU8String label = default, float width = 0.0f) + public bool Draw(ImU8String label = default, float width = 0.0f) { fixed (ImGuiTextFilter* thisPtr = &this) - ImGui.Draw(thisPtr, label, width); + return ImGui.Draw(thisPtr, label, width); } - public void PassFilter(ImU8String text) + public bool PassFilter(ImU8String text) { fixed (ImGuiTextFilter* thisPtr = &this) - ImGui.PassFilter(thisPtr, text); + return ImGui.PassFilter(thisPtr, text); } } public partial struct ImGuiTextFilterPtr { - public void Draw(ImU8String label = default, float width = 0.0f) => ImGui.Draw(this, label, width); - public void PassFilter(ImU8String text) => ImGui.PassFilter(this, text); + public bool Draw(ImU8String label = default, float width = 0.0f) => ImGui.Draw(this, label, width); + public bool PassFilter(ImU8String text) => ImGui.PassFilter(this, text); } diff --git a/imgui/Dalamud.Bindings.ImGui/Dalamud.Bindings.ImGui.csproj b/imgui/Dalamud.Bindings.ImGui/Dalamud.Bindings.ImGui.csproj index fc5894857..8668e9b8f 100644 --- a/imgui/Dalamud.Bindings.ImGui/Dalamud.Bindings.ImGui.csproj +++ b/imgui/Dalamud.Bindings.ImGui/Dalamud.Bindings.ImGui.csproj @@ -9,7 +9,7 @@ - + diff --git a/imgui/Dalamud.Bindings.ImGui/ImU8String.cs b/imgui/Dalamud.Bindings.ImGui/ImU8String.cs index d1b9ae6c2..a62152c39 100644 --- a/imgui/Dalamud.Bindings.ImGui/ImU8String.cs +++ b/imgui/Dalamud.Bindings.ImGui/ImU8String.cs @@ -8,7 +8,7 @@ using System.Text.Unicode; namespace Dalamud.Bindings.ImGui; [InterpolatedStringHandler] -public ref struct ImU8String : IDisposable +public ref struct ImU8String { public const int AllocFreeBufferSize = 512; private const int MinimumRentSize = AllocFreeBufferSize * 2; @@ -35,7 +35,7 @@ public ref struct ImU8String : IDisposable } public ImU8String(int literalLength, int formattedCount) - : this(ReadOnlySpan.Empty) + : this(""u8) { this.state |= State.Interpolation; literalLength += formattedCount * 4; @@ -51,6 +51,12 @@ public ref struct ImU8String : IDisposable public ImU8String(ReadOnlySpan text, bool ensureNullTermination = false) : this() { + if (Unsafe.IsNullRef(in MemoryMarshal.GetReference(text))) + { + this.state = State.None; + return; + } + this.state = State.Initialized; if (text.IsEmpty) { @@ -82,6 +88,12 @@ public ref struct ImU8String : IDisposable public ImU8String(ReadOnlySpan text) : this() { + if (Unsafe.IsNullRef(in MemoryMarshal.GetReference(text))) + { + this.state = State.None; + return; + } + this.state = State.Initialized | State.NullTerminated; this.Length = Encoding.UTF8.GetByteCount(text); if (this.Length + 1 < AllocFreeBufferSize) @@ -169,6 +181,28 @@ public ref struct ImU8String : IDisposable public static unsafe implicit operator ImU8String(byte* text) => new(text); public static unsafe implicit operator ImU8String(char* text) => new(text); + public ref readonly byte GetPinnableReference() + { + if (this.IsNull) + return ref Unsafe.NullRef(); + + if (this.IsEmpty) + return ref this.FixedBufferSpan[0]; + + return ref this.Span.GetPinnableReference(); + } + + public ref readonly byte GetPinnableReference(ReadOnlySpan defaultValue) + { + if (this.IsNull) + return ref defaultValue.GetPinnableReference(); + + if (this.IsEmpty) + return ref this.FixedBufferSpan[0]; + + return ref this.Span.GetPinnableReference(); + } + public ref readonly byte GetPinnableNullTerminatedReference(ReadOnlySpan defaultValue = default) { if (this.IsNull) @@ -213,7 +247,7 @@ public ref struct ImU8String : IDisposable this.externalFirstByte = ref Unsafe.NullRef(); } - public void Dispose() + public void Recycle() { if (this.rentedBuffer is { } buf) { @@ -226,7 +260,7 @@ public ref struct ImU8String : IDisposable { if (!this.IsNull) { - other.Dispose(); + other.Recycle(); var res = this; this = default; return res; @@ -267,7 +301,7 @@ public ref struct ImU8String : IDisposable { var startingPos = this.Length; this.AppendFormatted(value, format); - FixAlignment(startingPos, alignment); + this.FixAlignment(startingPos, alignment); } public void AppendFormatted(ReadOnlySpan value) => this.AppendFormatted(value, null); @@ -288,9 +322,15 @@ public ref struct ImU8String : IDisposable { var startingPos = this.Length; this.AppendFormatted(value, format); - FixAlignment(startingPos, alignment); + this.FixAlignment(startingPos, alignment); } + public void AppendFormatted(object? value) => this.AppendFormatted(value!); + public void AppendFormatted(object? value, string? format) => this.AppendFormatted(value!, format); + public void AppendFormatted(object? value, int alignment) => this.AppendFormatted(value!, alignment); + public void AppendFormatted(object? value, int alignment, string? format) => + this.AppendFormatted(value!, alignment, format); + public void AppendFormatted(T value) => this.AppendFormatted(value, null); public void AppendFormatted(T value, string? format) @@ -318,13 +358,13 @@ public ref struct ImU8String : IDisposable { var startingPos = this.Length; this.AppendFormatted(value, format); - FixAlignment(startingPos, alignment); + this.FixAlignment(startingPos, alignment); } public void Reserve(int length) { if (length >= AllocFreeBufferSize) - IncreaseBuffer(out _, length); + this.IncreaseBuffer(out _, length); } private void FixAlignment(int startingPos, int alignment) diff --git a/imgui/Dalamud.Bindings.ImGuizmo/Dalamud.Bindings.ImGuizmo.csproj b/imgui/Dalamud.Bindings.ImGuizmo/Dalamud.Bindings.ImGuizmo.csproj index 650381100..6cdd80470 100644 --- a/imgui/Dalamud.Bindings.ImGuizmo/Dalamud.Bindings.ImGuizmo.csproj +++ b/imgui/Dalamud.Bindings.ImGuizmo/Dalamud.Bindings.ImGuizmo.csproj @@ -9,7 +9,7 @@ - + diff --git a/imgui/Dalamud.Bindings.ImPlot/Dalamud.Bindings.ImPlot.csproj b/imgui/Dalamud.Bindings.ImPlot/Dalamud.Bindings.ImPlot.csproj index 650381100..6cdd80470 100644 --- a/imgui/Dalamud.Bindings.ImPlot/Dalamud.Bindings.ImPlot.csproj +++ b/imgui/Dalamud.Bindings.ImPlot/Dalamud.Bindings.ImPlot.csproj @@ -9,7 +9,7 @@ - + diff --git a/imgui/StandaloneImGuiTestbed/StandaloneImGuiTestbed.csproj b/imgui/StandaloneImGuiTestbed/StandaloneImGuiTestbed.csproj index 21a6a4c66..d56faa31e 100644 --- a/imgui/StandaloneImGuiTestbed/StandaloneImGuiTestbed.csproj +++ b/imgui/StandaloneImGuiTestbed/StandaloneImGuiTestbed.csproj @@ -1,10 +1,10 @@  - Exe enable enable true + false @@ -36,5 +36,4 @@ - diff --git a/lib/Directory.Packages.props b/lib/Directory.Packages.props new file mode 100644 index 000000000..bafcac533 --- /dev/null +++ b/lib/Directory.Packages.props @@ -0,0 +1,5 @@ + + + false + + diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 294254960..e5dedba42 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 2942549605a0b1c7dfb274afabfe7db0332415bc +Subproject commit e5dedba42a3fea8f050ea54ac583a5874bf51c6f diff --git a/lib/Lumina.Excel b/lib/Lumina.Excel new file mode 160000 index 000000000..5d01489c3 --- /dev/null +++ b/lib/Lumina.Excel @@ -0,0 +1 @@ +Subproject commit 5d01489c34f33a3d645f49085d7fc0065a1ac801 diff --git a/tools/Dalamud.LocExporter/Dalamud.LocExporter.csproj b/tools/Dalamud.LocExporter/Dalamud.LocExporter.csproj index aa6cabedc..c0d31c3d6 100644 --- a/tools/Dalamud.LocExporter/Dalamud.LocExporter.csproj +++ b/tools/Dalamud.LocExporter/Dalamud.LocExporter.csproj @@ -1,5 +1,4 @@  - Exe enable @@ -11,7 +10,6 @@ - + -